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 8024331 8024688
  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.Hashtable;
  40 import java.util.HashSet;
  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.concurrent.atomic.AtomicBoolean;
  52 import java.util.function.BiFunction;
  53 import java.util.function.Function;
  54 import java.util.function.Supplier;
  55 
  56 import org.testng.annotations.Test;
  57 import org.testng.annotations.DataProvider;
  58 import static java.util.Objects.requireNonNull;
  59 import static org.testng.Assert.fail;
  60 import static org.testng.Assert.assertEquals;
  61 import static org.testng.Assert.assertTrue;
  62 import static org.testng.Assert.assertFalse;
  63 import static org.testng.Assert.assertNull;
  64 import static org.testng.Assert.assertSame;
  65 
  66 public class Defaults {
  67 
  68     @Test(dataProvider = "Map<IntegerEnum,String> rw=all keys=withNull values=withNull")
  69     public void testGetOrDefaultNulls(String description, Map<IntegerEnum, String> map) {
  70         assertTrue(map.containsKey(null), description + ": null key absent");
  71         assertNull(map.get(null), description + ": value not null");
  72         assertSame(map.get(null), map.getOrDefault(null, EXTRA_VALUE), description + ": values should match");
  73     }
  74 
  75     @Test(dataProvider = "Map<IntegerEnum,String> rw=all keys=all values=all")
  76     public void testGetOrDefault(String description, Map<IntegerEnum, String> map) {
  77         assertTrue(map.containsKey(KEYS[1]), "expected key missing");
  78         assertSame(map.get(KEYS[1]), map.getOrDefault(KEYS[1], EXTRA_VALUE), "values should match");
  79         assertFalse(map.containsKey(EXTRA_KEY), "expected absent key");
  80         assertSame(map.getOrDefault(EXTRA_KEY, EXTRA_VALUE), EXTRA_VALUE, "value not returned as default");
  81         assertNull(map.getOrDefault(EXTRA_KEY, null), "null not returned as default");
  82     }
  83 
  84     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull")
  85     public void testPutIfAbsentNulls(String description, Map<IntegerEnum, String> map) {
  86         // null -> null
  87         assertTrue(map.containsKey(null), "null key absent");
  88         assertNull(map.get(null), "value not null");
  89         assertNull(map.putIfAbsent(null, EXTRA_VALUE), "previous not null");
  90         // null -> EXTRA_VALUE
  91         assertTrue(map.containsKey(null), "null key absent");
  92         assertSame(map.get(null), EXTRA_VALUE, "unexpected value");
  93         assertSame(map.putIfAbsent(null, null), EXTRA_VALUE, "previous not expected value");
  94         assertTrue(map.containsKey(null), "null key absent");
  95         assertSame(map.get(null), EXTRA_VALUE, "unexpected value");
  96         assertSame(map.remove(null), EXTRA_VALUE, "removed unexpected value");
  97         // null -> <absent>
  98 
  99         assertFalse(map.containsKey(null), description + ": key present after remove");
 100         assertNull(map.putIfAbsent(null, null), "previous not null");
 101         // null -> null
 102         assertTrue(map.containsKey(null), "null key absent");
 103         assertNull(map.get(null), "value not null");
 104         assertNull(map.putIfAbsent(null, EXTRA_VALUE), "previous not null");
 105         assertSame(map.get(null), EXTRA_VALUE, "value not expected");
 106     }
 107 
 108     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")
 109     public void testPutIfAbsent(String description, Map<IntegerEnum, String> map) {
 110         // 1 -> 1
 111         assertTrue(map.containsKey(KEYS[1]));
 112         Object expected = map.get(KEYS[1]);
 113         assertTrue(null == expected || expected == VALUES[1]);
 114         assertSame(map.putIfAbsent(KEYS[1], EXTRA_VALUE), expected);
 115         assertSame(map.get(KEYS[1]), expected);
 116 
 117         // EXTRA_KEY -> <absent>
 118         assertFalse(map.containsKey(EXTRA_KEY));
 119         assertSame(map.putIfAbsent(EXTRA_KEY, EXTRA_VALUE), null);
 120         assertSame(map.get(EXTRA_KEY), EXTRA_VALUE);
 121         assertSame(map.putIfAbsent(EXTRA_KEY, VALUES[2]), EXTRA_VALUE);
 122         assertSame(map.get(EXTRA_KEY), EXTRA_VALUE);
 123     }
 124 
 125     @Test(dataProvider = "Map<IntegerEnum,String> rw=all keys=all values=all")
 126     public void testForEach(String description, Map<IntegerEnum, String> map) {
 127         IntegerEnum[] EACH_KEY = new IntegerEnum[map.size()];
 128 
 129         map.forEach((k, v) -> {
 130             int idx = (null == k) ? 0 : k.ordinal(); // substitute for index.
 131             assertNull(EACH_KEY[idx]);
 132             EACH_KEY[idx] = (idx == 0) ? KEYS[0] : k; // substitute for comparison.
 133             assertSame(v, map.get(k));
 134         });
 135 
 136         assertEquals(KEYS, EACH_KEY, description);
 137     }
 138 
 139     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")
 140     public static void testReplaceAll(String description, Map<IntegerEnum, String> map) {
 141         IntegerEnum[] EACH_KEY = new IntegerEnum[map.size()];
 142         Set<String> EACH_REPLACE = new HashSet<>(map.size());
 143 
 144         map.replaceAll((k,v) -> {
 145             int idx = (null == k) ? 0 : k.ordinal(); // substitute for index.
 146             assertNull(EACH_KEY[idx]);
 147             EACH_KEY[idx] = (idx == 0) ? KEYS[0] : k; // substitute for comparison.
 148             assertSame(v, map.get(k));
 149             String replacement = v + " replaced";
 150             EACH_REPLACE.add(replacement);
 151             return replacement;
 152         });
 153 
 154         assertEquals(KEYS, EACH_KEY, description);
 155         assertEquals(map.values().size(), EACH_REPLACE.size(), description + EACH_REPLACE);
 156         assertTrue(EACH_REPLACE.containsAll(map.values()), description + " : " + EACH_REPLACE + " != " + map.values());
 157         assertTrue(map.values().containsAll(EACH_REPLACE), description + " : " + EACH_REPLACE + " != " + map.values());
 158     }
 159 
 160     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=nonNull values=nonNull")
 161     public static void testReplaceAllNoNullReplacement(String description, Map<IntegerEnum, String> map) {
 162         assertThrows(
 163             () -> { map.replaceAll(null); },
 164             NullPointerException.class,
 165             description);
 166         assertThrows(
 167             () -> { map.replaceAll((k,v) -> null); },
 168             NullPointerException.class,
 169             description + " should not allow replacement with null value");
 170     }
 171 
 172     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull")
 173     public static void testRemoveNulls(String description, Map<IntegerEnum, String> map) {
 174         assertTrue(map.containsKey(null), "null key absent");
 175         assertNull(map.get(null), "value not null");
 176         assertFalse(map.remove(null, EXTRA_VALUE), description);
 177         assertTrue(map.containsKey(null));
 178         assertNull(map.get(null));
 179         assertTrue(map.remove(null, null));
 180         assertFalse(map.containsKey(null));
 181         assertNull(map.get(null));
 182         assertFalse(map.remove(null, null));
 183     }
 184 
 185     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")
 186     public static void testRemove(String description, Map<IntegerEnum, String> map) {
 187         assertTrue(map.containsKey(KEYS[1]));
 188         Object expected = map.get(KEYS[1]);
 189         assertTrue(null == expected || expected == VALUES[1]);
 190         assertFalse(map.remove(KEYS[1], EXTRA_VALUE), description);
 191         assertSame(map.get(KEYS[1]), expected);
 192         assertTrue(map.remove(KEYS[1], expected));
 193         assertNull(map.get(KEYS[1]));
 194         assertFalse(map.remove(KEYS[1], expected));
 195 
 196         assertFalse(map.containsKey(EXTRA_KEY));
 197         assertFalse(map.remove(EXTRA_KEY, EXTRA_VALUE));
 198     }
 199 
 200     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull")
 201     public void testReplaceKVNulls(String description, Map<IntegerEnum, String> map) {
 202         assertTrue(map.containsKey(null), "null key absent");
 203         assertNull(map.get(null), "value not null");
 204         assertSame(map.replace(null, EXTRA_VALUE), null);
 205         assertSame(map.get(null), EXTRA_VALUE);
 206     }
 207 
 208     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=nonNull values=nonNull")
 209     public void testReplaceKVNoNulls(String description, Map<IntegerEnum, String> map) {
 210         assertTrue(map.containsKey(FIRST_KEY), "expected key missing");
 211         assertSame(map.get(FIRST_KEY), FIRST_VALUE, "found wrong value");
 212         assertThrows( () -> {map.replace(FIRST_KEY, null);}, NullPointerException.class, description + ": should throw NPE");
 213         assertSame(map.replace(FIRST_KEY, EXTRA_VALUE), FIRST_VALUE, description + ": replaced wrong value");
 214         assertSame(map.get(FIRST_KEY), EXTRA_VALUE, "found wrong value");
 215     }
 216 
 217     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")
 218     public void testReplaceKV(String description, Map<IntegerEnum, String> map) {
 219         assertTrue(map.containsKey(KEYS[1]));
 220         Object expected = map.get(KEYS[1]);
 221         assertTrue(null == expected || expected == VALUES[1]);
 222         assertSame(map.replace(KEYS[1], EXTRA_VALUE), expected);
 223         assertSame(map.get(KEYS[1]), EXTRA_VALUE);
 224 
 225         assertFalse(map.containsKey(EXTRA_KEY));
 226         assertNull(map.replace(EXTRA_KEY, EXTRA_VALUE));
 227         assertFalse(map.containsKey(EXTRA_KEY));
 228         assertNull(map.get(EXTRA_KEY));
 229         assertNull(map.put(EXTRA_KEY, EXTRA_VALUE));
 230         assertSame(map.get(EXTRA_KEY), EXTRA_VALUE);
 231         assertSame(map.replace(EXTRA_KEY, (String)expected), EXTRA_VALUE);
 232         assertSame(map.get(EXTRA_KEY), expected);
 233     }
 234 
 235     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull")
 236     public void testReplaceKVVNulls(String description, Map<IntegerEnum, String> map) {
 237         assertTrue(map.containsKey(null), "null key absent");
 238         assertNull(map.get(null), "value not null");
 239         assertFalse(map.replace(null, EXTRA_VALUE, EXTRA_VALUE));
 240         assertNull(map.get(null));
 241         assertTrue(map.replace(null, null, EXTRA_VALUE));
 242         assertSame(map.get(null), EXTRA_VALUE);
 243         assertTrue(map.replace(null, EXTRA_VALUE, EXTRA_VALUE));
 244         assertSame(map.get(null), EXTRA_VALUE);
 245     }
 246 
 247     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=nonNull values=nonNull")
 248     public void testReplaceKVVNoNulls(String description, Map<IntegerEnum, String> map) {
 249         assertTrue(map.containsKey(FIRST_KEY), "expected key missing");
 250         assertSame(map.get(FIRST_KEY), FIRST_VALUE, "found wrong value");
 251         assertThrows( () -> {map.replace(FIRST_KEY, FIRST_VALUE, null);}, NullPointerException.class, description + ": should throw NPE");
 252         assertThrows( () -> {if (!map.replace(FIRST_KEY, null, EXTRA_VALUE)) throw new NullPointerException("default returns false rather than throwing");}, NullPointerException.class,  description + ": should throw NPE");
 253         assertTrue(map.replace(FIRST_KEY, FIRST_VALUE, EXTRA_VALUE), description + ": replaced wrong value");
 254         assertSame(map.get(FIRST_KEY), EXTRA_VALUE, "found wrong value");
 255     }
 256 
 257     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")
 258     public void testReplaceKVV(String description, Map<IntegerEnum, String> map) {
 259         assertTrue(map.containsKey(KEYS[1]));
 260         Object expected = map.get(KEYS[1]);
 261         assertTrue(null == expected || expected == VALUES[1]);
 262         assertFalse(map.replace(KEYS[1], EXTRA_VALUE, EXTRA_VALUE));
 263         assertSame(map.get(KEYS[1]), expected);
 264         assertTrue(map.replace(KEYS[1], (String)expected, EXTRA_VALUE));
 265         assertSame(map.get(KEYS[1]), EXTRA_VALUE);
 266         assertTrue(map.replace(KEYS[1], EXTRA_VALUE, EXTRA_VALUE));
 267         assertSame(map.get(KEYS[1]), EXTRA_VALUE);
 268 
 269         assertFalse(map.containsKey(EXTRA_KEY));
 270         assertFalse(map.replace(EXTRA_KEY, EXTRA_VALUE, EXTRA_VALUE));
 271         assertFalse(map.containsKey(EXTRA_KEY));
 272         assertNull(map.get(EXTRA_KEY));
 273         assertNull(map.put(EXTRA_KEY, EXTRA_VALUE));
 274         assertTrue(map.containsKey(EXTRA_KEY));
 275         assertSame(map.get(EXTRA_KEY), EXTRA_VALUE);
 276         assertTrue(map.replace(EXTRA_KEY, EXTRA_VALUE, EXTRA_VALUE));
 277         assertSame(map.get(EXTRA_KEY), EXTRA_VALUE);
 278     }
 279 
 280     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull")
 281     public void testComputeIfAbsentNulls(String description, Map<IntegerEnum, String> map) {
 282         // null -> null
 283         assertTrue(map.containsKey(null), "null key absent");
 284         assertNull(map.get(null), "value not null");
 285         assertSame(map.computeIfAbsent(null, (k) -> null), null,  "not expected result");
 286         assertTrue(map.containsKey(null), "null key absent");
 287         assertNull(map.get(null), "value not null");
 288         assertSame(map.computeIfAbsent(null, (k) -> EXTRA_VALUE), EXTRA_VALUE, "not mapped to result");
 289         // null -> EXTRA_VALUE
 290         assertTrue(map.containsKey(null), "null key absent");
 291         assertSame(map.get(null), EXTRA_VALUE,  "not expected value");
 292         assertSame(map.remove(null), EXTRA_VALUE, "removed unexpected value");
 293         // null -> <absent>
 294         assertFalse(map.containsKey(null), "null key present");
 295         assertSame(map.computeIfAbsent(null, (k) -> EXTRA_VALUE), EXTRA_VALUE, "not mapped to result");
 296         // null -> EXTRA_VALUE
 297         assertTrue(map.containsKey(null), "null key absent");
 298         assertSame(map.get(null), EXTRA_VALUE,  "not expected value");
 299     }
 300 
 301     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")
 302     public void testComputeIfAbsent(String description, Map<IntegerEnum, String> map) {
 303         // 1 -> 1
 304         assertTrue(map.containsKey(KEYS[1]));
 305         Object expected = map.get(KEYS[1]);
 306         assertTrue(null == expected || expected == VALUES[1], description + String.valueOf(expected));
 307         expected = (null == expected) ? EXTRA_VALUE : expected;
 308         assertSame(map.computeIfAbsent(KEYS[1], (k) -> EXTRA_VALUE), expected, description);
 309         assertSame(map.get(KEYS[1]), expected, description);
 310 
 311         // EXTRA_KEY -> <absent>
 312         assertFalse(map.containsKey(EXTRA_KEY));
 313         assertNull(map.computeIfAbsent(EXTRA_KEY, (k) -> null));
 314         assertFalse(map.containsKey(EXTRA_KEY));
 315         assertSame(map.computeIfAbsent(EXTRA_KEY, (k) -> EXTRA_VALUE), EXTRA_VALUE);
 316         // EXTRA_KEY -> EXTRA_VALUE
 317         assertSame(map.get(EXTRA_KEY), EXTRA_VALUE);
 318     }
 319 
 320     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")
 321     public void testComputeIfAbsentNullFunction(String description, Map<IntegerEnum, String> map) {
 322         assertThrows( () -> { map.computeIfAbsent(KEYS[1], null);},
 323                 NullPointerException.class,
 324                 "Should throw NPE");
 325     }
 326 
 327     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull")
 328     public void testComputeIfPresentNulls(String description, Map<IntegerEnum, String> map) {
 329         assertTrue(map.containsKey(null), description + ": null key absent");
 330         assertNull(map.get(null), description + ": value not null");
 331         assertSame(map.computeIfPresent(null, (k, v) -> {
 332             fail(description + ": null value is not deemed present");
 333             return EXTRA_VALUE;
 334         }), null, description);
 335         assertTrue(map.containsKey(null));
 336         assertNull(map.get(null), description);
 337         assertNull(map.remove(EXTRA_KEY), description + ": unexpected mapping");
 338         assertNull(map.put(EXTRA_KEY, null), description + ": unexpected value");
 339         assertSame(map.computeIfPresent(EXTRA_KEY, (k, v) -> {
 340             fail(description + ": null value is not deemed present");
 341             return EXTRA_VALUE;
 342         }), null, description);
 343         assertNull(map.get(EXTRA_KEY), description + ": null mapping gone");
 344     }
 345 
 346     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")
 347     public void testComputeIfPresent(String description, Map<IntegerEnum, String> map) {
 348         assertTrue(map.containsKey(KEYS[1]));
 349         Object value = map.get(KEYS[1]);
 350         assertTrue(null == value || value == VALUES[1], description + String.valueOf(value));
 351         Object expected = (null == value) ? null : EXTRA_VALUE;
 352         assertSame(map.computeIfPresent(KEYS[1], (k, v) -> {
 353             assertSame(v, value);
 354             return EXTRA_VALUE;
 355         }), expected, description);
 356         assertSame(map.get(KEYS[1]), expected, description);
 357 
 358         assertFalse(map.containsKey(EXTRA_KEY));
 359         assertSame(map.computeIfPresent(EXTRA_KEY, (k, v) -> {
 360             fail();
 361             return EXTRA_VALUE;
 362         }), null);
 363         assertFalse(map.containsKey(EXTRA_KEY));
 364         assertSame(map.get(EXTRA_KEY), null);
 365     }
 366 
 367     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")
 368     public void testComputeIfPresentNullFunction(String description, Map<IntegerEnum, String> map) {
 369         assertThrows( () -> { map.computeIfPresent(KEYS[1], null);},
 370                 NullPointerException.class,
 371                 "Should throw NPE");
 372     }
 373 
 374      @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull")
 375     public void testComputeNulls(String description, Map<IntegerEnum, String> map) {
 376         assertTrue(map.containsKey(null), "null key absent");
 377         assertNull(map.get(null), "value not null");
 378         assertSame(map.compute(null, (k, v) -> {
 379             assertNull(k);
 380             assertNull(v);
 381             return null;
 382         }), null, description);
 383         assertFalse(map.containsKey(null), description + ": null key present.");
 384         assertSame(map.compute(null, (k, v) -> {
 385             assertSame(k, null);
 386             assertNull(v);
 387             return EXTRA_VALUE;
 388         }), EXTRA_VALUE, description);
 389         assertTrue(map.containsKey(null));
 390         assertSame(map.get(null), EXTRA_VALUE, description);
 391         assertSame(map.remove(null), EXTRA_VALUE, description + ": removed value not expected");
 392         // no mapping before and after
 393         assertFalse(map.containsKey(null), description + ": null key present");
 394         assertSame(map.compute(null, (k, v) -> {
 395             assertNull(k);
 396             assertNull(v);
 397             return null;
 398         }), null, description + ": expected null result" );
 399         assertFalse(map.containsKey(null), description + ": null key present");
 400         // compute with map not containing value
 401         assertNull(map.remove(EXTRA_KEY),  description + ": unexpected mapping");
 402         assertFalse(map.containsKey(EXTRA_KEY),  description + ": key present");
 403         assertSame(map.compute(EXTRA_KEY, (k, v) -> {
 404             assertSame(k, EXTRA_KEY);
 405             assertNull(v);
 406             return null;
 407         }), null, description);
 408         assertFalse(map.containsKey(EXTRA_KEY),  description + ": null key present");
 409         // ensure removal.
 410         assertNull(map.put(EXTRA_KEY, EXTRA_VALUE));
 411         assertSame(map.compute(EXTRA_KEY, (k, v) -> {
 412             assertSame(k, EXTRA_KEY);
 413             assertSame(v, EXTRA_VALUE);
 414             return null;
 415         }), null, description + ": null resulted expected");
 416         assertFalse(map.containsKey(EXTRA_KEY),  description + ": null key present");
 417        // compute with map containing null value
 418         assertNull(map.put(EXTRA_KEY, null),  description + ": unexpected value");
 419         assertSame(map.compute(EXTRA_KEY, (k, v) -> {
 420             assertSame(k, EXTRA_KEY);
 421             assertNull(v);
 422             return null;
 423         }), null, description);
 424         assertFalse(map.containsKey(EXTRA_KEY),  description + ": null key present");
 425         assertNull(map.put(EXTRA_KEY, null),  description + ": unexpected value");
 426         assertSame(map.compute(EXTRA_KEY, (k, v) -> {
 427             assertSame(k, EXTRA_KEY);
 428             assertNull(v);
 429             return EXTRA_VALUE;
 430         }), EXTRA_VALUE, description);
 431         assertTrue(map.containsKey(EXTRA_KEY), "null key present");
 432     }
 433 
 434     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")
 435     public void testCompute(String description, Map<IntegerEnum, String> map) {
 436         assertTrue(map.containsKey(KEYS[1]));
 437         Object value = map.get(KEYS[1]);
 438         assertTrue(null == value || value == VALUES[1], description + String.valueOf(value));
 439         assertSame(map.compute(KEYS[1], (k, v) -> {
 440             assertSame(k, KEYS[1]);
 441             assertSame(v, value);
 442             return EXTRA_VALUE;
 443         }), EXTRA_VALUE, description);
 444         assertSame(map.get(KEYS[1]), EXTRA_VALUE, description);
 445         assertNull(map.compute(KEYS[1], (k, v) -> {
 446             assertSame(v, EXTRA_VALUE);
 447             return null;
 448         }), description);
 449         assertFalse(map.containsKey(KEYS[1]));
 450 
 451         assertFalse(map.containsKey(EXTRA_KEY));
 452         assertSame(map.compute(EXTRA_KEY, (k, v) -> {
 453             assertNull(v);
 454             return EXTRA_VALUE;
 455         }), EXTRA_VALUE);
 456         assertTrue(map.containsKey(EXTRA_KEY));
 457         assertSame(map.get(EXTRA_KEY), EXTRA_VALUE);
 458     }
 459 
 460     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")
 461     public void testComputeNullFunction(String description, Map<IntegerEnum, String> map) {
 462         assertThrows( () -> { map.compute(KEYS[1], null);},
 463                 NullPointerException.class,
 464                 "Should throw NPE");
 465     }
 466 
 467     @Test(dataProvider = "MergeCases")
 468     private void testMerge(String description, Map<IntegerEnum, String> map, Merging.Value oldValue, Merging.Value newValue, Merging.Merger merger, Merging.Value put, Merging.Value result) {
 469             // add and check initial conditions.
 470             switch (oldValue) {
 471                 case ABSENT :
 472                     map.remove(EXTRA_KEY);
 473                     assertFalse(map.containsKey(EXTRA_KEY), "key not absent");
 474                     break;
 475                 case NULL :
 476                     map.put(EXTRA_KEY, null);
 477                     assertTrue(map.containsKey(EXTRA_KEY), "key absent");
 478                     assertNull(map.get(EXTRA_KEY), "wrong value");
 479                     break;
 480                 case OLDVALUE :
 481                     map.put(EXTRA_KEY, VALUES[1]);
 482                     assertTrue(map.containsKey(EXTRA_KEY), "key absent");
 483                     assertSame(map.get(EXTRA_KEY), VALUES[1], "wrong value");
 484                     break;
 485                 default:
 486                     fail("unexpected old value");
 487             }
 488 
 489             String returned = map.merge(EXTRA_KEY,
 490                 newValue == Merging.Value.NULL ? (String) null : VALUES[2],
 491                 merger
 492                 );
 493 
 494             // check result
 495 
 496             switch (result) {
 497                 case NULL :
 498                     assertNull(returned, "wrong value");
 499                     break;
 500                 case NEWVALUE :
 501                     assertSame(returned, VALUES[2], "wrong value");
 502                     break;
 503                 case RESULT :
 504                     assertSame(returned, VALUES[3], "wrong value");
 505                     break;
 506                 default:
 507                     fail("unexpected new value");
 508             }
 509 
 510             // check map
 511             switch (put) {
 512                 case ABSENT :
 513                     assertFalse(map.containsKey(EXTRA_KEY), "key not absent");
 514                     break;
 515                 case NULL :
 516                     assertTrue(map.containsKey(EXTRA_KEY), "key absent");
 517                     assertNull(map.get(EXTRA_KEY), "wrong value");
 518                     break;
 519                 case NEWVALUE :
 520                     assertTrue(map.containsKey(EXTRA_KEY), "key absent");
 521                     assertSame(map.get(EXTRA_KEY), VALUES[2], "wrong value");
 522                     break;
 523                 case RESULT :
 524                     assertTrue(map.containsKey(EXTRA_KEY), "key absent");
 525                     assertSame(map.get(EXTRA_KEY), VALUES[3], "wrong value");
 526                     break;
 527                 default:
 528                     fail("unexpected new value");
 529             }
 530     }
 531 
 532     @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")
 533     public void testMergeNullMerger(String description, Map<IntegerEnum, String> map) {
 534         assertThrows( () -> { map.merge(KEYS[1], VALUES[1], null);},
 535                 NullPointerException.class,
 536                 "Should throw NPE");
 537     }
 538 
 539     /** A function that flipflops between running two other functions. */
 540     static <T,U,V> BiFunction<T,U,V> twoStep(AtomicBoolean b,
 541                                              BiFunction<T,U,V> first,
 542                                              BiFunction<T,U,V> second) {
 543         return (t, u) -> {
 544             boolean bb = b.get();
 545             try {
 546                 return (b.get() ? first : second).apply(t, u);
 547             } finally {
 548                 b.set(!bb);
 549             }};
 550     }
 551 
 552     /**
 553      * Simulates races by modifying the map within the mapping function.
 554      */
 555     @Test
 556     public void testConcurrentMap_computeIfAbsent_racy() {
 557         final ConcurrentMap<Long,Long> map = new ImplementsConcurrentMap<>();
 558         final Long two = 2L;
 559         Function<Long,Long> f, g;
 560 
 561         // race not detected if function returns null
 562         f = (k) -> { map.put(two, 42L); return null; };
 563         assertNull(map.computeIfAbsent(two, f));
 564         assertEquals(42L, (long)map.get(two));
 565 
 566         map.clear();
 567         f = (k) -> { map.put(two, 42L); return 86L; };
 568         assertEquals(42L, (long)map.computeIfAbsent(two, f));
 569         assertEquals(42L, (long)map.get(two));
 570 
 571         // mapping function ignored if value already exists
 572         map.put(two, 99L);
 573         assertEquals(99L, (long)map.computeIfAbsent(two, f));
 574         assertEquals(99L, (long)map.get(two));
 575     }
 576 
 577     /**
 578      * Simulates races by modifying the map within the remapping function.
 579      */
 580     @Test
 581     public void testConcurrentMap_computeIfPresent_racy() {
 582         final AtomicBoolean b = new AtomicBoolean(true);
 583         final ConcurrentMap<Long,Long> map = new ImplementsConcurrentMap<>();
 584         final Long two = 2L;
 585         BiFunction<Long,Long,Long> f, g;
 586 
 587         for (Long val : new Long[] { null, 86L }) {
 588             map.clear();
 589 
 590             // Function not invoked if no mapping exists
 591             f = (k, v) -> { map.put(two, 42L); return val; };
 592             assertNull(map.computeIfPresent(two, f));
 593             assertNull(map.get(two));
 594 
 595             map.put(two, 42L);
 596             f = (k, v) -> { map.put(two, 86L); return val; };
 597             g = (k, v) -> {
 598                 assertSame(two, k);
 599                 assertEquals(86L, (long)v);
 600                 return null;
 601             };
 602             assertNull(map.computeIfPresent(two, twoStep(b, f, g)));
 603             assertFalse(map.containsKey(two));
 604             assertTrue(b.get());
 605 
 606             map.put(two, 42L);
 607             f = (k, v) -> { map.put(two, 86L); return val; };
 608             g = (k, v) -> {
 609                 assertSame(two, k);
 610                 assertEquals(86L, (long)v);
 611                 return 99L;
 612             };
 613             assertEquals(99L, (long)map.computeIfPresent(two, twoStep(b, f, g)));
 614             assertTrue(map.containsKey(two));
 615             assertTrue(b.get());
 616         }
 617     }
 618 
 619     @Test
 620     public void testConcurrentMap_compute_simple() {
 621         final ConcurrentMap<Long,Long> map = new ImplementsConcurrentMap<>();
 622         BiFunction<Long,Long,Long> fun = (k, v) -> ((v == null) ? 0L : k + v);
 623         assertEquals(Long.valueOf(0L), map.compute(3L, fun));
 624         assertEquals(Long.valueOf(3L), map.compute(3L, fun));
 625         assertEquals(Long.valueOf(6L), map.compute(3L, fun));
 626         assertNull(map.compute(3L, (k, v) -> null));
 627         assertTrue(map.isEmpty());
 628 
 629         assertEquals(Long.valueOf(0L), map.compute(new Long(3L), fun));
 630         assertEquals(Long.valueOf(3L), map.compute(new Long(3L), fun));
 631         assertEquals(Long.valueOf(6L), map.compute(new Long(3L), fun));
 632         assertNull(map.compute(3L, (k, v) -> null));
 633         assertTrue(map.isEmpty());
 634     }
 635 
 636     /**
 637      * Simulates races by modifying the map within the remapping function.
 638      */
 639     @Test
 640     public void testConcurrentMap_compute_racy() {
 641         final AtomicBoolean b = new AtomicBoolean(true);
 642         final ConcurrentMap<Long,Long> map = new ImplementsConcurrentMap<>();
 643         final Long two = 2L;
 644         BiFunction<Long,Long,Long> f, g;
 645 
 646         // null -> null is a no-op; race not detected
 647         f = (k, v) -> { map.put(two, 42L); return null; };
 648         assertNull(map.compute(two, f));
 649         assertEquals(42L, (long)map.get(two));
 650 
 651         for (Long val : new Long[] { null, 86L }) {
 652             map.clear();
 653 
 654             f = (k, v) -> { map.put(two, 42L); return 86L; };
 655             g = (k, v) -> {
 656                 assertSame(two, k);
 657                 assertEquals(42L, (long)v);
 658                 return k + v;
 659             };
 660             assertEquals(44L, (long)map.compute(two, twoStep(b, f, g)));
 661             assertEquals(44L, (long)map.get(two));
 662             assertTrue(b.get());
 663 
 664             f = (k, v) -> { map.remove(two); return val; };
 665             g = (k, v) -> {
 666                 assertSame(two, k);
 667                 assertNull(v);
 668                 return 44L;
 669             };
 670             assertEquals(44L, (long)map.compute(two, twoStep(b, f, g)));
 671             assertEquals(44L, (long)map.get(two));
 672             assertTrue(map.containsKey(two));
 673             assertTrue(b.get());
 674 
 675             f = (k, v) -> { map.remove(two); return val; };
 676             g = (k, v) -> {
 677                 assertSame(two, k);
 678                 assertNull(v);
 679                 return null;
 680             };
 681             assertNull(map.compute(two, twoStep(b, f, g)));
 682             assertNull(map.get(two));
 683             assertFalse(map.containsKey(two));
 684             assertTrue(b.get());
 685         }
 686     }
 687 
 688     /**
 689      * Simulates races by modifying the map within the remapping function.
 690      */
 691     @Test
 692     public void testConcurrentMap_merge_racy() {
 693         final AtomicBoolean b = new AtomicBoolean(true);
 694         final ConcurrentMap<Long,Long> map = new ImplementsConcurrentMap<>();
 695         final Long two = 2L;
 696         BiFunction<Long,Long,Long> f, g;
 697 
 698         for (Long val : new Long[] { null, 86L }) {
 699             map.clear();
 700 
 701             f = (v, w) -> { throw new AssertionError(); };
 702             assertEquals(99L, (long)map.merge(two, 99L, f));
 703             assertEquals(99L, (long)map.get(two));
 704 
 705             f = (v, w) -> { map.put(two, 42L); return val; };
 706             g = (v, w) -> {
 707                 assertEquals(42L, (long)v);
 708                 assertEquals(3L, (long)w);
 709                 return v + w;
 710             };
 711             assertEquals(45L, (long)map.merge(two, 3L, twoStep(b, f, g)));
 712             assertEquals(45L, (long)map.get(two));
 713             assertTrue(b.get());
 714 
 715             f = (v, w) -> { map.remove(two); return val; };
 716             g = (k, v) -> { throw new AssertionError(); };
 717             assertEquals(55L, (long)map.merge(two, 55L, twoStep(b, f, g)));
 718             assertEquals(55L, (long)map.get(two));
 719             assertTrue(map.containsKey(two));
 720             assertFalse(b.get()); b.set(true);
 721         }
 722     }
 723 
 724     public enum IntegerEnum {
 725 
 726         e0, e1, e2, e3, e4, e5, e6, e7, e8, e9,
 727         e10, e11, e12, e13, e14, e15, e16, e17, e18, e19,
 728         e20, e21, e22, e23, e24, e25, e26, e27, e28, e29,
 729         e30, e31, e32, e33, e34, e35, e36, e37, e38, e39,
 730         e40, e41, e42, e43, e44, e45, e46, e47, e48, e49,
 731         e50, e51, e52, e53, e54, e55, e56, e57, e58, e59,
 732         e60, e61, e62, e63, e64, e65, e66, e67, e68, e69,
 733         e70, e71, e72, e73, e74, e75, e76, e77, e78, e79,
 734         e80, e81, e82, e83, e84, e85, e86, e87, e88, e89,
 735         e90, e91, e92, e93, e94, e95, e96, e97, e98, e99,
 736         EXTRA_KEY;
 737         public static final int SIZE = values().length;
 738     };
 739     private static final int TEST_SIZE = IntegerEnum.SIZE - 1;
 740     /**
 741      * Realized keys ensure that there is always a hard ref to all test objects.
 742      */
 743     private static final IntegerEnum[] KEYS = new IntegerEnum[TEST_SIZE];
 744     /**
 745      * Realized values ensure that there is always a hard ref to all test
 746      * objects.
 747      */
 748     private static final String[] VALUES = new String[TEST_SIZE];
 749 
 750     static {
 751         IntegerEnum[] keys = IntegerEnum.values();
 752         for (int each = 0; each < TEST_SIZE; each++) {
 753             KEYS[each] = keys[each];
 754             VALUES[each] = String.valueOf(each);
 755         }
 756     }
 757 
 758     private static final IntegerEnum FIRST_KEY = KEYS[0];
 759     private static final String FIRST_VALUE = VALUES[0];
 760     private static final IntegerEnum EXTRA_KEY = IntegerEnum.EXTRA_KEY;
 761     private static final String EXTRA_VALUE = String.valueOf(TEST_SIZE);
 762 
 763     @DataProvider(name = "Map<IntegerEnum,String> rw=all keys=all values=all", parallel = true)
 764     public static Iterator<Object[]> allMapProvider() {
 765         return makeAllMaps().iterator();
 766     }
 767 
 768     @DataProvider(name = "Map<IntegerEnum,String> rw=all keys=withNull values=withNull", parallel = true)
 769     public static Iterator<Object[]> allMapWithNullsProvider() {
 770         return makeAllMapsWithNulls().iterator();
 771     }
 772 
 773     @DataProvider(name = "Map<IntegerEnum,String> rw=true keys=nonNull values=nonNull", parallel = true)
 774     public static Iterator<Object[]> rwNonNullMapProvider() {
 775         return makeRWNoNullsMaps().iterator();
 776     }
 777 
 778     @DataProvider(name = "Map<IntegerEnum,String> rw=true keys=nonNull values=all", parallel = true)
 779     public static Iterator<Object[]> rwNonNullKeysMapProvider() {
 780         return makeRWMapsNoNulls().iterator();
 781     }
 782 
 783     @DataProvider(name = "Map<IntegerEnum,String> rw=true keys=all values=all", parallel = true)
 784     public static Iterator<Object[]> rwMapProvider() {
 785         return makeAllRWMaps().iterator();
 786     }
 787 
 788     @DataProvider(name = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull", parallel = true)
 789     public static Iterator<Object[]> rwNullsMapProvider() {
 790         return makeAllRWMapsWithNulls().iterator();
 791     }
 792 
 793     private static Collection<Object[]> makeAllRWMapsWithNulls() {
 794         Collection<Object[]> all = new ArrayList<>();
 795 
 796         all.addAll(makeRWMaps(true, true));
 797 
 798         return all;
 799     }
 800 
 801     private static Collection<Object[]> makeRWMapsNoNulls() {
 802         Collection<Object[]> all = new ArrayList<>();
 803 
 804         all.addAll(makeRWNoNullKeysMaps(false));
 805         all.addAll(makeRWNoNullsMaps());
 806 
 807         return all;
 808     }
 809 
 810     private static Collection<Object[]> makeAllROMaps() {
 811         Collection<Object[]> all = new ArrayList<>();
 812 
 813         all.addAll(makeROMaps(false));
 814         all.addAll(makeROMaps(true));
 815 
 816         return all;
 817     }
 818 
 819     private static Collection<Object[]> makeAllRWMaps() {
 820         Collection<Object[]> all = new ArrayList<>();
 821 
 822         all.addAll(makeRWNoNullsMaps());
 823         all.addAll(makeRWMaps(false,true));
 824         all.addAll(makeRWMaps(true,true));
 825         all.addAll(makeRWNoNullKeysMaps(true));
 826         return all;
 827     }
 828 
 829     private static Collection<Object[]> makeAllMaps() {
 830         Collection<Object[]> all = new ArrayList<>();
 831 
 832         all.addAll(makeAllROMaps());
 833         all.addAll(makeAllRWMaps());
 834 
 835         return all;
 836     }
 837 
 838     private static Collection<Object[]> makeAllMapsWithNulls() {
 839         Collection<Object[]> all = new ArrayList<>();
 840 
 841         all.addAll(makeROMaps(true));
 842         all.addAll(makeRWMaps(true,true));
 843 
 844         return all;
 845     }
 846 
 847     /**
 848      * @param nullKeys include null keys
 849      * @param nullValues include null values
 850      * @return
 851      */
 852     private static Collection<Object[]> makeRWMaps(boolean nullKeys, boolean nullValues) {
 853         return Arrays.asList(
 854             new Object[]{"HashMap", makeMap(HashMap::new, nullKeys, nullValues)},
 855             new Object[]{"IdentityHashMap", makeMap(IdentityHashMap::new, nullKeys, nullValues)},
 856             new Object[]{"LinkedHashMap", makeMap(LinkedHashMap::new, nullKeys, nullValues)},
 857             new Object[]{"WeakHashMap", makeMap(WeakHashMap::new, nullKeys, nullValues)},
 858             new Object[]{"Collections.checkedMap(HashMap)", Collections.checkedMap(makeMap(HashMap::new, nullKeys, nullValues), IntegerEnum.class, String.class)},
 859             new Object[]{"Collections.synchronizedMap(HashMap)", Collections.synchronizedMap(makeMap(HashMap::new, nullKeys, nullValues))},
 860             new Object[]{"ExtendsAbstractMap", makeMap(ExtendsAbstractMap::new, nullKeys, nullValues)});
 861     }
 862 
 863     /**
 864      * @param nulls include null values
 865      * @return
 866      */
 867     private static Collection<Object[]> makeRWNoNullKeysMaps(boolean nulls) {
 868         return Arrays.asList(
 869                 // null key hostile
 870                 new Object[]{"EnumMap", makeMap(() -> new EnumMap(IntegerEnum.class), false, nulls)},
 871                 new Object[]{"TreeMap", makeMap(TreeMap::new, false, nulls)},
 872                 new Object[]{"ExtendsAbstractMap(TreeMap)", makeMap(() -> {return new ExtendsAbstractMap(new TreeMap());}, false, nulls)},
 873                 new Object[]{"Collections.synchronizedMap(EnumMap)", Collections.synchronizedMap(makeMap(() -> new EnumMap(IntegerEnum.class), false, nulls))}
 874                 );
 875     }
 876 
 877     private static Collection<Object[]> makeRWNoNullsMaps() {
 878         return Arrays.asList(
 879             // null key and value hostile
 880             new Object[]{"Hashtable", makeMap(Hashtable::new, false, false)},
 881             new Object[]{"ConcurrentHashMap", makeMap(ConcurrentHashMap::new, false, false)},
 882             new Object[]{"ConcurrentSkipListMap", makeMap(ConcurrentSkipListMap::new, false, false)},
 883             new Object[]{"Collections.synchronizedMap(ConcurrentHashMap)", Collections.synchronizedMap(makeMap(ConcurrentHashMap::new, false, false))},
 884             new Object[]{"Collections.checkedMap(ConcurrentHashMap)", Collections.checkedMap(makeMap(ConcurrentHashMap::new, false, false), IntegerEnum.class, String.class)},
 885             new Object[]{"ExtendsAbstractMap(ConcurrentHashMap)", makeMap(() -> {return new ExtendsAbstractMap(new ConcurrentHashMap());}, false, false)},
 886             new Object[]{"ImplementsConcurrentMap", makeMap(ImplementsConcurrentMap::new, false, false)}
 887             );
 888     }
 889 
 890     /**
 891      * @param nulls include nulls
 892      * @return
 893      */
 894     private static Collection<Object[]> makeROMaps(boolean nulls) {
 895         return Arrays.asList(new Object[][]{
 896             new Object[]{"Collections.unmodifiableMap(HashMap)", Collections.unmodifiableMap(makeMap(HashMap::new, nulls, nulls))}
 897         });
 898     }
 899 
 900     /**
 901      * @param supplier a supplier of mutable map instances.
 902      *
 903      * @param nullKeys   include null keys
 904      * @param nullValues include null values
 905      * @return
 906      */
 907     private static Map<IntegerEnum, String> makeMap(Supplier<Map<IntegerEnum, String>> supplier, boolean nullKeys, boolean nullValues) {
 908         Map<IntegerEnum, String> result = supplier.get();
 909 
 910         for (int each = 0; each < TEST_SIZE; each++) {
 911             IntegerEnum key = nullKeys ? (each == 0) ? null : KEYS[each] : KEYS[each];
 912             String value = nullValues ? (each == 0) ? null : VALUES[each] : VALUES[each];
 913 
 914             result.put(key, value);
 915         }
 916 
 917         return result;
 918     }
 919 
 920     static class Merging {
 921         public enum Value {
 922             ABSENT,
 923             NULL,
 924             OLDVALUE,
 925             NEWVALUE,
 926             RESULT
 927         }
 928 
 929         public enum Merger implements BiFunction<String,String,String> {
 930             UNUSED {
 931                 public String apply(String oldValue, String newValue) {
 932                     fail("should not be called");
 933                     return null;
 934                 }
 935             },
 936             NULL {
 937                 public String apply(String oldValue, String newValue) {
 938                     return null;
 939                 }
 940             },
 941             RESULT {
 942                 public String apply(String oldValue, String newValue) {
 943                     return VALUES[3];
 944                 }
 945             },
 946         }
 947     }
 948 
 949     @DataProvider(name = "MergeCases", parallel = true)
 950     public Iterator<Object[]> mergeCasesProvider() {
 951         Collection<Object[]> cases = new ArrayList<>();
 952 
 953         cases.addAll(makeMergeTestCases());
 954 
 955         return cases.iterator();
 956     }
 957 
 958     static Collection<Object[]> makeMergeTestCases() {
 959         Collection<Object[]> cases = new ArrayList<>();
 960 
 961         for (Object[] mapParams : makeAllRWMaps() ) {
 962             cases.add(new Object[] { mapParams[0], mapParams[1], Merging.Value.ABSENT, Merging.Value.NEWVALUE, Merging.Merger.UNUSED, Merging.Value.NEWVALUE, Merging.Value.NEWVALUE });
 963         }
 964 
 965         for (Object[] mapParams : makeAllRWMaps() ) {
 966             cases.add(new Object[] { mapParams[0], mapParams[1], Merging.Value.OLDVALUE, Merging.Value.NEWVALUE, Merging.Merger.NULL, Merging.Value.ABSENT, Merging.Value.NULL });
 967         }
 968 
 969         for (Object[] mapParams : makeAllRWMaps() ) {
 970             cases.add(new Object[] { mapParams[0], mapParams[1], Merging.Value.OLDVALUE, Merging.Value.NEWVALUE, Merging.Merger.RESULT, Merging.Value.RESULT, Merging.Value.RESULT });
 971         }
 972 
 973         return cases;
 974     }
 975 
 976     public interface Thrower<T extends Throwable> {
 977 
 978         public void run() throws T;
 979     }
 980 
 981     public static <T extends Throwable> void assertThrows(Thrower<T> thrower, Class<T> throwable) {
 982         assertThrows(thrower, throwable, null);
 983     }
 984 
 985     public static <T extends Throwable> void assertThrows(Thrower<T> thrower, Class<T> throwable, String message) {
 986         Throwable thrown;
 987         try {
 988             thrower.run();
 989             thrown = null;
 990         } catch (Throwable caught) {
 991             thrown = caught;
 992         }
 993 
 994         assertInstance(thrown, throwable,
 995             ((null != message) ? message : "") +
 996             " Failed to throw " + throwable.getCanonicalName());
 997     }
 998 
 999     public static <T extends Throwable> void assertThrows(Class<T> throwable, String message, Thrower<T>... throwers) {
1000         for (Thrower<T> thrower : throwers) {
1001             assertThrows(thrower, throwable, message);
1002         }
1003     }
1004 
1005     public static void assertInstance(Object actual, Class<?> expected) {
1006         assertInstance(expected.isInstance(actual), null);
1007     }
1008 
1009     public static void assertInstance(Object actual, Class<?> expected, String message) {
1010         assertTrue(expected.isInstance(actual), message);
1011     }
1012 
1013     /**
1014      * A simple mutable map implementation that provides only default
1015      * implementations of all methods. ie. none of the Map interface default
1016      * methods have overridden implementations.
1017      *
1018      * @param <K> Type of keys
1019      * @param <V> Type of values
1020      */
1021     public static class ExtendsAbstractMap<M extends Map<K,V>, K, V> extends AbstractMap<K,V> {
1022 
1023         protected final M map;
1024 
1025         public ExtendsAbstractMap() { this( (M) new HashMap<K,V>()); }
1026 
1027         protected ExtendsAbstractMap(M map) { this.map = map; }
1028 
1029         @Override public Set<Map.Entry<K,V>> entrySet() {
1030             return new AbstractSet<Map.Entry<K,V>>() {
1031                 @Override public int size() {
1032                     return map.size();
1033                 }
1034 
1035                 @Override public Iterator<Map.Entry<K,V>> iterator() {
1036                     final Iterator<Map.Entry<K,V>> source = map.entrySet().iterator();
1037                     return new Iterator<Map.Entry<K,V>>() {
1038                        public boolean hasNext() { return source.hasNext(); }
1039                        public Map.Entry<K,V> next() { return source.next(); }
1040                        public void remove() { source.remove(); }
1041                     };
1042                 }
1043 
1044                 @Override public boolean add(Map.Entry<K,V> e) {
1045                     return map.entrySet().add(e);
1046                 }
1047             };
1048         }
1049 
1050         @Override public V put(K key, V value) {
1051             return map.put(key, value);
1052         }
1053     }
1054 
1055     /**
1056      * A simple mutable concurrent map implementation that provides only default
1057      * implementations of all methods, i.e. none of the ConcurrentMap interface
1058      * default methods have overridden implementations.
1059      *
1060      * @param <K> Type of keys
1061      * @param <V> Type of values
1062      */
1063     public static class ImplementsConcurrentMap<K,V> extends ExtendsAbstractMap<ConcurrentMap<K,V>, K, V> implements ConcurrentMap<K,V> {
1064         public ImplementsConcurrentMap() { super(new ConcurrentHashMap<K,V>()); }
1065 
1066         // ConcurrentMap reabstracts these methods.
1067         //
1068         // Unlike ConcurrentHashMap, we have zero tolerance for null values.
1069 
1070         @Override public V replace(K k, V v) {
1071             return map.replace(requireNonNull(k), requireNonNull(v));
1072         }
1073 
1074         @Override public boolean replace(K k, V v, V vv) {
1075             return map.replace(requireNonNull(k),
1076                                requireNonNull(v),
1077                                requireNonNull(vv));
1078         }
1079 
1080         @Override public boolean remove(Object k, Object v) {
1081             return map.remove(requireNonNull(k), requireNonNull(v));
1082         }
1083 
1084         @Override public V putIfAbsent(K k, V v) {
1085             return map.putIfAbsent(requireNonNull(k), requireNonNull(v));
1086         }
1087     }
1088 }