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);
 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=all values=all")
 198     public void testReplaceKV(String description, Map<IntegerEnum, String> map) {
 199         assertTrue(map.containsKey(KEYS[1]));
 200         Object expected = map.get(KEYS[1]);
 201         assertTrue(null == expected || expected == VALUES[1]);
 202         assertSame(map.replace(KEYS[1], EXTRA_VALUE), expected);
 203         assertSame(map.get(KEYS[1]), EXTRA_VALUE);
 204 
 205         assertFalse(map.containsKey(EXTRA_KEY));
 206         assertNull(map.replace(EXTRA_KEY, EXTRA_VALUE));
 207         assertFalse(map.containsKey(EXTRA_KEY));
 208         assertNull(map.get(EXTRA_KEY));
 209         assertNull(map.put(EXTRA_KEY, EXTRA_VALUE));
 210         assertSame(map.get(EXTRA_KEY), EXTRA_VALUE);
 211         assertSame(map.replace(EXTRA_KEY, (String)expected), EXTRA_VALUE);
 212         assertSame(map.get(EXTRA_KEY), expected);
 213     }
 214 
 215     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull")
 216     public void testReplaceKVVNulls(String description, Map<IntegerEnum, String> map) {
 217         assertTrue(map.containsKey(null), "null key absent");
 218         assertNull(map.get(null), "value not null");
 219         assertFalse(map.replace(null, EXTRA_VALUE, EXTRA_VALUE));
 220         assertNull(map.get(null));
 221         assertTrue(map.replace(null, null, EXTRA_VALUE));
 222         assertSame(map.get(null), EXTRA_VALUE);
 223         assertTrue(map.replace(null, EXTRA_VALUE, EXTRA_VALUE));
 224         assertSame(map.get(null), EXTRA_VALUE);
 225     }
 226 
 227     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")
 228     public void testReplaceKVV(String description, Map<IntegerEnum, String> map) {
 229         assertTrue(map.containsKey(KEYS[1]));
 230         Object expected = map.get(KEYS[1]);
 231         assertTrue(null == expected || expected == VALUES[1]);
 232         assertFalse(map.replace(KEYS[1], EXTRA_VALUE, EXTRA_VALUE));
 233         assertSame(map.get(KEYS[1]), expected);
 234         assertTrue(map.replace(KEYS[1], (String)expected, EXTRA_VALUE));
 235         assertSame(map.get(KEYS[1]), EXTRA_VALUE);
 236         assertTrue(map.replace(KEYS[1], EXTRA_VALUE, EXTRA_VALUE));
 237         assertSame(map.get(KEYS[1]), EXTRA_VALUE);
 238 
 239         assertFalse(map.containsKey(EXTRA_KEY));
 240         assertFalse(map.replace(EXTRA_KEY, EXTRA_VALUE, EXTRA_VALUE));
 241         assertFalse(map.containsKey(EXTRA_KEY));
 242         assertNull(map.get(EXTRA_KEY));
 243         assertNull(map.put(EXTRA_KEY, EXTRA_VALUE));
 244         assertTrue(map.containsKey(EXTRA_KEY));
 245         assertSame(map.get(EXTRA_KEY), EXTRA_VALUE);
 246         assertTrue(map.replace(EXTRA_KEY, EXTRA_VALUE, EXTRA_VALUE));
 247         assertSame(map.get(EXTRA_KEY), EXTRA_VALUE);
 248     }
 249 
 250     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull")
 251     public void testComputeIfAbsentNulls(String description, Map<IntegerEnum, String> map) {
 252         assertTrue(map.containsKey(null), "null key absent");
 253         assertNull(map.get(null), "value not null");
 254         assertSame(map.computeIfAbsent(null, (k) -> EXTRA_VALUE), EXTRA_VALUE, description);
 255         assertSame(map.get(null), EXTRA_VALUE, description);
 256     }
 257 
 258     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")
 259     public void testComputeIfAbsent(String description, Map<IntegerEnum, String> map) {
 260         assertTrue(map.containsKey(KEYS[1]));
 261         Object expected = map.get(KEYS[1]);
 262         assertTrue(null == expected || expected == VALUES[1], description + String.valueOf(expected));
 263         expected = (null == expected) ? EXTRA_VALUE : expected;
 264         assertSame(map.computeIfAbsent(KEYS[1], (k) -> EXTRA_VALUE), expected, description);
 265         assertSame(map.get(KEYS[1]), expected, description);
 266 
 267         assertFalse(map.containsKey(EXTRA_KEY));
 268         assertSame(map.computeIfAbsent(EXTRA_KEY, (k) -> EXTRA_VALUE), EXTRA_VALUE);
 269         assertSame(map.get(EXTRA_KEY), EXTRA_VALUE);
 270     }
 271 
 272     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull")
 273     public void testComputeIfPresentNulls(String description, Map<IntegerEnum, String> map) {
 274         assertTrue(map.containsKey(null), description + ": null key absent");
 275         assertNull(map.get(null), description + ": value not null");
 276         assertSame(map.computeIfPresent(null, (k, v) -> {
 277             fail(description + ": null value is not deemed present");
 278             return EXTRA_VALUE;
 279         }), null, description);
 280         assertTrue(map.containsKey(null));
 281         assertNull(map.get(null), description);
 282         assertNull(map.remove(EXTRA_KEY), description + ": unexpected mapping");
 283         assertNull(map.put(EXTRA_KEY, null), description + ": unexpected value");
 284         assertSame(map.computeIfPresent(EXTRA_KEY, (k, v) -> {
 285             fail(description + ": null value is not deemed present");
 286             return EXTRA_VALUE;
 287         }), null, description);
 288         assertNull(map.get(EXTRA_KEY), description + ": null mapping gone");
 289     }
 290 
 291     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")
 292     public void testComputeIfPresent(String description, Map<IntegerEnum, String> map) {
 293         assertTrue(map.containsKey(KEYS[1]));
 294         Object value = map.get(KEYS[1]);
 295         assertTrue(null == value || value == VALUES[1], description + String.valueOf(value));
 296         Object expected = (null == value) ? null : EXTRA_VALUE;
 297         assertSame(map.computeIfPresent(KEYS[1], (k, v) -> {
 298             assertSame(v, value);
 299             return EXTRA_VALUE;
 300         }), expected, description);
 301         assertSame(map.get(KEYS[1]), expected, description);
 302 
 303         assertFalse(map.containsKey(EXTRA_KEY));
 304         assertSame(map.computeIfPresent(EXTRA_KEY, (k, v) -> {
 305             fail();
 306             return EXTRA_VALUE;
 307         }), null);
 308         assertFalse(map.containsKey(EXTRA_KEY));
 309         assertSame(map.get(EXTRA_KEY), null);
 310     }
 311 
 312     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull")
 313     public void testComputeNulls(String description, Map<IntegerEnum, String> map) {
 314         assertTrue(map.containsKey(null), "null key absent");
 315         assertNull(map.get(null), "value not null");
 316         assertSame(map.compute(null, (k, v) -> {
 317             assertNull(k);
 318             assertNull(v);
 319             return null;
 320         }), null, description);
 321         assertFalse(map.containsKey(null), description + ": null key present.");
 322         assertSame(map.compute(null, (k, v) -> {
 323             assertSame(k, null);
 324             assertNull(v);
 325             return EXTRA_VALUE;
 326         }), EXTRA_VALUE, description);
 327         assertTrue(map.containsKey(null));
 328         assertSame(map.get(null), EXTRA_VALUE, description);
 329         assertSame(map.remove(null), EXTRA_VALUE, description + ": removed value not expected");
 330         // no mapping before and after
 331         assertFalse(map.containsKey(null), description + ": null key present");
 332         assertSame(map.compute(null, (k, v) -> {
 333             assertNull(k);
 334             assertNull(v);
 335             return null;
 336         }), null, description + ": expected null result" );
 337         assertFalse(map.containsKey(null), description + ": null key present");
 338         // compute with map not containing value
 339         assertNull(map.remove(EXTRA_KEY),  description + ": unexpected mapping");
 340         assertFalse(map.containsKey(EXTRA_KEY),  description + ": key present");
 341         assertSame(map.compute(EXTRA_KEY, (k, v) -> {
 342             assertSame(k, EXTRA_KEY);
 343             assertNull(v);
 344             return null;
 345         }), null, description);
 346         assertFalse(map.containsKey(EXTRA_KEY),  description + ": null key present");
 347         // ensure removal.
 348         assertNull(map.put(EXTRA_KEY, EXTRA_VALUE));
 349         assertSame(map.compute(EXTRA_KEY, (k, v) -> {
 350             assertSame(k, EXTRA_KEY);
 351             assertSame(v, EXTRA_VALUE);
 352             return null;
 353         }), null, description + ": null resulted expected");
 354         assertFalse(map.containsKey(EXTRA_KEY),  description + ": null key present");
 355        // compute with map containing null value
 356         assertNull(map.put(EXTRA_KEY, null),  description + ": unexpected value");
 357         assertSame(map.compute(EXTRA_KEY, (k, v) -> {
 358             assertSame(k, EXTRA_KEY);
 359             assertNull(v);
 360             return null;
 361         }), null, description);
 362         assertFalse(map.containsKey(EXTRA_KEY),  description + ": null key present");
 363         assertNull(map.put(EXTRA_KEY, null),  description + ": unexpected value");
 364         assertSame(map.compute(EXTRA_KEY, (k, v) -> {
 365             assertSame(k, EXTRA_KEY);
 366             assertNull(v);
 367             return EXTRA_VALUE;
 368         }), EXTRA_VALUE, description);
 369         assertTrue(map.containsKey(EXTRA_KEY), "null key present");
 370     }
 371 
 372     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")
 373     public void testCompute(String description, Map<IntegerEnum, String> map) {
 374         assertTrue(map.containsKey(KEYS[1]));
 375         Object value = map.get(KEYS[1]);
 376         assertTrue(null == value || value == VALUES[1], description + String.valueOf(value));
 377         assertSame(map.compute(KEYS[1], (k, v) -> {
 378             assertSame(k, KEYS[1]);
 379             assertSame(v, value);
 380             return EXTRA_VALUE;
 381         }), EXTRA_VALUE, description);
 382         assertSame(map.get(KEYS[1]), EXTRA_VALUE, description);
 383         assertNull(map.compute(KEYS[1], (k, v) -> {
 384             assertSame(v, EXTRA_VALUE);
 385             return null;
 386         }), description);
 387         assertFalse(map.containsKey(KEYS[1]));
 388 
 389         assertFalse(map.containsKey(EXTRA_KEY));
 390         assertSame(map.compute(EXTRA_KEY, (k, v) -> {
 391             assertNull(v);
 392             return EXTRA_VALUE;
 393         }), EXTRA_VALUE);
 394         assertTrue(map.containsKey(EXTRA_KEY));
 395         assertSame(map.get(EXTRA_KEY), EXTRA_VALUE);
 396     }
 397 
 398 
 399     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull")
 400     public void testMergeNulls(String description, Map<IntegerEnum, String> map) {
 401         assertTrue(map.containsKey(null), "null key absent");
 402         assertNull(map.get(null), "value not null");
 403         assertSame(map.merge(null, EXTRA_VALUE, (v, vv) -> {
 404             assertNull(v);
 405             assertSame(vv, EXTRA_VALUE);
 406             return vv;
 407         }), EXTRA_VALUE, description);
 408         assertTrue(map.containsKey(null));
 409         assertSame(map.get(null), EXTRA_VALUE, description);
 410     }
 411 
 412     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")
 413     public void testMerge(String description, Map<IntegerEnum, String> map) {
 414         assertTrue(map.containsKey(KEYS[1]));
 415         Object value = map.get(KEYS[1]);
 416         assertTrue(null == value || value == VALUES[1], description + String.valueOf(value));
 417         assertSame(map.merge(KEYS[1], EXTRA_VALUE, (v, vv) -> {
 418             assertSame(v, value);
 419             assertSame(vv, EXTRA_VALUE);
 420             return vv;
 421         }), EXTRA_VALUE, description);
 422         assertSame(map.get(KEYS[1]), EXTRA_VALUE, description);
 423         assertNull(map.merge(KEYS[1], EXTRA_VALUE, (v, vv) -> {
 424             assertSame(v, EXTRA_VALUE);
 425             assertSame(vv, EXTRA_VALUE);
 426             return null;
 427         }), description);
 428         assertFalse(map.containsKey(KEYS[1]));
 429 
 430         assertFalse(map.containsKey(EXTRA_KEY));
 431         assertSame(map.merge(EXTRA_KEY, EXTRA_VALUE, (v, vv) -> {
 432             assertNull(v);
 433             assertSame(vv, EXTRA_VALUE);
 434             return EXTRA_VALUE;
 435         }), EXTRA_VALUE);
 436         assertTrue(map.containsKey(EXTRA_KEY));
 437         assertSame(map.get(EXTRA_KEY), EXTRA_VALUE);
 438     }
 439 
 440     enum IntegerEnum {
 441 
 442         e0, e1, e2, e3, e4, e5, e6, e7, e8, e9,
 443         e10, e11, e12, e13, e14, e15, e16, e17, e18, e19,
 444         e20, e21, e22, e23, e24, e25, e26, e27, e28, e29,
 445         e30, e31, e32, e33, e34, e35, e36, e37, e38, e39,
 446         e40, e41, e42, e43, e44, e45, e46, e47, e48, e49,
 447         e50, e51, e52, e53, e54, e55, e56, e57, e58, e59,
 448         e60, e61, e62, e63, e64, e65, e66, e67, e68, e69,
 449         e70, e71, e72, e73, e74, e75, e76, e77, e78, e79,
 450         e80, e81, e82, e83, e84, e85, e86, e87, e88, e89,
 451         e90, e91, e92, e93, e94, e95, e96, e97, e98, e99,
 452         EXTRA_KEY;
 453         public static final int SIZE = values().length;
 454     };
 455     private static final int TEST_SIZE = IntegerEnum.SIZE - 1;
 456     /**
 457      * Realized keys ensure that there is always a hard ref to all test objects.
 458      */
 459     private static final IntegerEnum[] KEYS = new IntegerEnum[TEST_SIZE];
 460     /**
 461      * Realized values ensure that there is always a hard ref to all test
 462      * objects.
 463      */
 464     private static final String[] VALUES = new String[TEST_SIZE];
 465 
 466     static {
 467         IntegerEnum[] keys = IntegerEnum.values();
 468         for (int each = 0; each < TEST_SIZE; each++) {
 469             KEYS[each] = keys[each];
 470             VALUES[each] = String.valueOf(each);
 471         }
 472     }
 473     private static final IntegerEnum EXTRA_KEY = IntegerEnum.EXTRA_KEY;
 474     private static final String EXTRA_VALUE = String.valueOf(TEST_SIZE);
 475 
 476     @DataProvider(name = "Map<IntegerEnum,String> rw=all keys=all values=all", parallel = true)
 477     public static Iterator<Object[]> allMapProvider() {
 478         return makeAllMaps().iterator();
 479     }
 480 
 481     @DataProvider(name = "Map<IntegerEnum,String> rw=all keys=withNull values=withNull", parallel = true)
 482     public static Iterator<Object[]> allMapWithNullsProvider() {
 483         return makeAllMapsWithNulls().iterator();
 484     }
 485 
 486     @DataProvider(name = "Map<IntegerEnum,String> rw=true keys=nonNull values=nonNull", parallel = true)
 487     public static Iterator<Object[]> rwNonNullMapProvider() {
 488         return makeRWNoNullsMaps().iterator();
 489     }
 490 
 491     @DataProvider(name = "Map<IntegerEnum,String> rw=true keys=nonNull values=all", parallel = true)
 492     public static Iterator<Object[]> rwNonNullKeysMapProvider() {
 493         return makeRWMapsNoNulls().iterator();
 494     }
 495 
 496     @DataProvider(name = "Map<IntegerEnum,String> rw=true keys=all values=all", parallel = true)
 497     public static Iterator<Object[]> rwMapProvider() {
 498         return makeAllRWMaps().iterator();
 499     }
 500 
 501     @DataProvider(name = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull", parallel = true)
 502     public static Iterator<Object[]> rwNullsMapProvider() {
 503         return makeAllRWMapsWithNulls().iterator();
 504     }
 505 
 506     private static Collection<Object[]> makeAllRWMapsWithNulls() {
 507         Collection<Object[]> all = new ArrayList<>();
 508 
 509         all.addAll(makeRWMaps(true, true));
 510 
 511         return all;
 512     }
 513 
 514 
 515     private static Collection<Object[]> makeRWMapsNoNulls() {
 516         Collection<Object[]> all = new ArrayList<>();
 517 
 518         all.addAll(makeRWNoNullKeysMaps(false));
 519         all.addAll(makeRWNoNullsMaps());
 520 
 521         return all;
 522     }
 523 
 524     private static Collection<Object[]> makeAllROMaps() {
 525         Collection<Object[]> all = new ArrayList<>();
 526 
 527         all.addAll(makeROMaps(false));
 528         all.addAll(makeROMaps(true));
 529 
 530         return all;
 531     }
 532 
 533     private static Collection<Object[]> makeAllRWMaps() {
 534         Collection<Object[]> all = new ArrayList<>();
 535 
 536         all.addAll(makeRWNoNullsMaps());
 537         all.addAll(makeRWMaps(false,true));
 538         all.addAll(makeRWMaps(true,true));
 539         all.addAll(makeRWNoNullKeysMaps(true));
 540         return all;
 541     }
 542 
 543     private static Collection<Object[]> makeAllMaps() {
 544         Collection<Object[]> all = new ArrayList<>();
 545 
 546         all.addAll(makeAllROMaps());
 547         all.addAll(makeAllRWMaps());
 548 
 549         return all;
 550     }
 551 
 552     private static Collection<Object[]> makeAllMapsWithNulls() {
 553         Collection<Object[]> all = new ArrayList<>();
 554 
 555         all.addAll(makeROMaps(true));
 556         all.addAll(makeRWMaps(true,true));
 557 
 558         return all;
 559     }
 560     /**
 561      *
 562      * @param nullKeys include null keys
 563      * @param nullValues include null values
 564      * @return
 565      */
 566     private static Collection<Object[]> makeRWMaps(boolean nullKeys, boolean nullValues) {
 567         return Arrays.asList(
 568             new Object[]{"HashMap", makeMap(HashMap::new, nullKeys, nullValues)},
 569             new Object[]{"IdentityHashMap", makeMap(IdentityHashMap::new, nullKeys, nullValues)},
 570             new Object[]{"LinkedHashMap", makeMap(LinkedHashMap::new, nullKeys, nullValues)},
 571             new Object[]{"WeakHashMap", makeMap(WeakHashMap::new, nullKeys, nullValues)},
 572             new Object[]{"Collections.checkedMap(HashMap)", Collections.checkedMap(makeMap(HashMap::new, nullKeys, nullValues), IntegerEnum.class, String.class)},
 573             new Object[]{"Collections.synchronizedMap(HashMap)", Collections.synchronizedMap(makeMap(HashMap::new, nullKeys, nullValues))},
 574             new Object[]{"ExtendsAbstractMap", makeMap(ExtendsAbstractMap::new, nullKeys, nullValues)});
 575     }
 576 
 577     /**
 578      *
 579      * @param nulls include null values
 580      * @return
 581      */
 582     private static Collection<Object[]> makeRWNoNullKeysMaps(boolean nulls) {
 583         return Arrays.asList(
 584                 // null key hostile
 585                 new Object[]{"EnumMap", makeMap(() -> new EnumMap(IntegerEnum.class), false, nulls)},
 586                 new Object[]{"Collections.synchronizedMap(EnumMap)", Collections.synchronizedMap(makeMap(() -> new EnumMap(IntegerEnum.class), false, nulls))}
 587                 );
 588     }
 589 
 590     private static Collection<Object[]> makeRWNoNullsMaps() {
 591         return Arrays.asList(
 592             // null key and value hostile
 593             new Object[]{"Hashtable", makeMap(Hashtable::new, false, false)},
 594             new Object[]{"TreeMap", makeMap(TreeMap::new, false, false)},
 595             new Object[]{"ConcurrentHashMap", makeMap(ConcurrentHashMap::new, false, false)},
 596             new Object[]{"ConcurrentSkipListMap", makeMap(ConcurrentSkipListMap::new, false, false)},
 597             new Object[]{"Collections.checkedMap(ConcurrentHashMap)", Collections.checkedMap(makeMap(ConcurrentHashMap::new, false, false), IntegerEnum.class, String.class)},
 598             new Object[]{"ImplementsConcurrentMap", makeMap(ImplementsConcurrentMap::new, false, false)}
 599             );
 600     }
 601 
 602     /**
 603      *
 604      * @param nulls include nulls
 605      * @return
 606      */
 607     private static Collection<Object[]> makeROMaps(boolean nulls) {
 608         return Arrays.asList(new Object[][]{
 609             new Object[]{"Collections.unmodifiableMap(HashMap)", Collections.unmodifiableMap(makeMap(HashMap::new, nulls, nulls))}
 610         });
 611     }
 612 
 613      /**
 614      *
 615      * @param supplier a supplier of mutable map instances.
 616      *
 617      * @param nullKeys   include null keys
 618      * @param nullValues include null values
 619      * @return
 620      */
 621     private static Map<IntegerEnum, String> makeMap(Supplier<Map<IntegerEnum, String>> supplier, boolean nullKeys, boolean nullValues) {
 622         Map<IntegerEnum, String> result = supplier.get();
 623 
 624         for (int each = 0; each < TEST_SIZE; each++) {
 625             IntegerEnum key = nullKeys ? (each == 0) ? null : KEYS[each] : KEYS[each];
 626             String value = nullValues ? (each == 0) ? null : VALUES[each] : VALUES[each];
 627 
 628             result.put(key, value);
 629         }
 630 
 631         return result;
 632     }
 633 
 634     public interface Thrower<T extends Throwable> {
 635 
 636         public void run() throws T;
 637     }
 638 
 639     public static <T extends Throwable> void assertThrows(Thrower<T> thrower, Class<T> throwable) {
 640         assertThrows(thrower, throwable, null);
 641     }
 642 
 643     public static <T extends Throwable> void assertThrows(Thrower<T> thrower, Class<T> throwable, String message) {
 644         Throwable result;
 645         try {
 646             thrower.run();
 647             result = null;
 648         } catch (Throwable caught) {
 649             result = caught;
 650         }
 651 
 652         assertInstance(result, throwable,
 653             (null != message)
 654             ? message
 655             : "Failed to throw " + throwable.getCanonicalName());
 656     }
 657 
 658     public static <T extends Throwable> void assertThrows(Class<T> throwable, String message, Thrower<T>... throwers) {
 659         for(Thrower<T> thrower : throwers) {
 660             assertThrows(thrower, throwable, message);
 661         }
 662     }
 663 
 664     public static <T> void assertInstance(T actual, Class<? extends T> expected) {
 665         assertInstance(expected.isInstance(actual), null);
 666     }
 667 
 668     public static <T> void assertInstance(T actual, Class<? extends T> expected, String message) {
 669         assertTrue(expected.isInstance(actual), message);
 670     }
 671 
 672     /**
 673      * A simple mutable map implementation that provides only default
 674      * implementations of all methods. ie. none of the Map interface default
 675      * methods have overridden implementations.
 676      *
 677      * @param <K> Type of keys
 678      * @param <V> Type of values
 679      */
 680     public static class ExtendsAbstractMap<M extends Map<K,V>, K, V> extends AbstractMap<K, V> {
 681 
 682         protected final M map;
 683 
 684         public ExtendsAbstractMap() { this( (M) new HashMap<K,V>()); }
 685 
 686         protected ExtendsAbstractMap(M map) { this.map = map; }
 687 
 688         public Set<Map.Entry<K, V>> entrySet() {
 689             return new AbstractSet<Map.Entry<K, V>>() {
 690                 public int size() {
 691                     return map.size();
 692                 }
 693 
 694                 public Iterator<Map.Entry<K,V>> iterator() {
 695                     final Iterator<Map.Entry<K,V>> source = map.entrySet().iterator();
 696                     return new Iterator<Map.Entry<K,V>>() {
 697                        public boolean hasNext() { return source.hasNext(); }
 698                        public Map.Entry<K,V> next() { return source.next(); }
 699                        public void remove() { source.remove(); }
 700                     };
 701                 }
 702 
 703                 public boolean add(Map.Entry<K,V> e) {
 704                     return map.entrySet().add(e);
 705                 }
 706             };
 707         }
 708 
 709         public V put(K key, V value) {
 710             return map.put(key, value);
 711         }
 712     }
 713 
 714     /**
 715      * A simple mutable concurrent map implementation that provides only default
 716      * implementations of all methods. ie. none of the ConcurrentMap interface
 717      * default methods have overridden implementations.
 718      *
 719      * @param <K> Type of keys
 720      * @param <V> Type of values
 721      */
 722     public static class ImplementsConcurrentMap<K, V> extends ExtendsAbstractMap<ConcurrentMap<K,V>, K, V> implements ConcurrentMap<K,V> {
 723         public ImplementsConcurrentMap() { super(new ConcurrentHashMap<K,V>()); }
 724 
 725         // ConcurrentMap reabstracts these methods
 726 
 727         public V replace(K k, V v) { return map.replace(k, v); };
 728 
 729         public boolean replace(K k, V v, V vv) { return map.replace(k, v, vv); };
 730 
 731         public boolean remove(Object k, Object v) { return map.remove(k, v); }
 732 
 733         public V putIfAbsent(K k, V v) { return map.putIfAbsent(k, v); }
 734     }
 735 }