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 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(expectedExceptions = {NullPointerException.class}) 292 public void testComputeIfAbsentNPEHashMap() { 293 Object value = new HashMap().computeIfAbsent(KEYS[1], null); 294 } 295 296 @Test(expectedExceptions = {NullPointerException.class}) 297 public void testComputeIfAbsentNPEHashtable() { 298 Object value = new Hashtable().computeIfAbsent(KEYS[1], null); 299 } 300 301 @Test(expectedExceptions = {NullPointerException.class}) 302 public void testComputeIfAbsentNPETreeMap() { 303 Object value = new TreeMap().computeIfAbsent(KEYS[1], null); 304 } 305 306 @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull") 307 public void testComputeIfPresentNulls(String description, Map<IntegerEnum, String> map) { 308 assertTrue(map.containsKey(null), description + ": null key absent"); 309 assertNull(map.get(null), description + ": value not null"); 310 assertSame(map.computeIfPresent(null, (k, v) -> { 311 fail(description + ": null value is not deemed present"); 312 return EXTRA_VALUE; 313 }), null, description); 314 assertTrue(map.containsKey(null)); 315 assertNull(map.get(null), description); 316 assertNull(map.remove(EXTRA_KEY), description + ": unexpected mapping"); 317 assertNull(map.put(EXTRA_KEY, null), description + ": unexpected value"); 318 assertSame(map.computeIfPresent(EXTRA_KEY, (k, v) -> { 319 fail(description + ": null value is not deemed present"); 320 return EXTRA_VALUE; 321 }), null, description); 322 assertNull(map.get(EXTRA_KEY), description + ": null mapping gone"); 323 } 324 325 @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all") 326 public void testComputeIfPresent(String description, Map<IntegerEnum, String> map) { 327 assertTrue(map.containsKey(KEYS[1])); 328 Object value = map.get(KEYS[1]); 329 assertTrue(null == value || value == VALUES[1], description + String.valueOf(value)); 330 Object expected = (null == value) ? null : EXTRA_VALUE; 331 assertSame(map.computeIfPresent(KEYS[1], (k, v) -> { 332 assertSame(v, value); 333 return EXTRA_VALUE; 334 }), expected, description); 335 assertSame(map.get(KEYS[1]), expected, description); 336 337 assertFalse(map.containsKey(EXTRA_KEY)); 338 assertSame(map.computeIfPresent(EXTRA_KEY, (k, v) -> { 339 fail(); 340 return EXTRA_VALUE; 341 }), null); 342 assertFalse(map.containsKey(EXTRA_KEY)); 343 assertSame(map.get(EXTRA_KEY), null); 344 } 345 346 @Test(expectedExceptions = {NullPointerException.class}) 347 public void testComputeIfPresentNPEHashMap() { 348 Object value = new HashMap().computeIfPresent(KEYS[1], null); 349 } 350 351 @Test(expectedExceptions = {NullPointerException.class}) 352 public void testComputeIfPresentNPEHashtable() { 353 Object value = new Hashtable().computeIfPresent(KEYS[1], null); 354 } 355 356 @Test(expectedExceptions = {NullPointerException.class}) 357 public void testComputeIfPresentNPETreeMap() { 358 Object value = new TreeMap().computeIfPresent(KEYS[1], null); 359 } 360 361 @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull") 362 public void testComputeNulls(String description, Map<IntegerEnum, String> map) { 363 assertTrue(map.containsKey(null), "null key absent"); 364 assertNull(map.get(null), "value not null"); 365 assertSame(map.compute(null, (k, v) -> { 366 assertNull(k); 367 assertNull(v); 368 return null; 369 }), null, description); 370 assertFalse(map.containsKey(null), description + ": null key present."); 371 assertSame(map.compute(null, (k, v) -> { 372 assertSame(k, null); 373 assertNull(v); 374 return EXTRA_VALUE; 375 }), EXTRA_VALUE, description); 376 assertTrue(map.containsKey(null)); 377 assertSame(map.get(null), EXTRA_VALUE, description); 378 assertSame(map.remove(null), EXTRA_VALUE, description + ": removed value not expected"); 379 // no mapping before and after 380 assertFalse(map.containsKey(null), description + ": null key present"); 381 assertSame(map.compute(null, (k, v) -> { 382 assertNull(k); 383 assertNull(v); 384 return null; 385 }), null, description + ": expected null result" ); 386 assertFalse(map.containsKey(null), description + ": null key present"); 387 // compute with map not containing value 388 assertNull(map.remove(EXTRA_KEY), description + ": unexpected mapping"); 389 assertFalse(map.containsKey(EXTRA_KEY), description + ": key present"); 390 assertSame(map.compute(EXTRA_KEY, (k, v) -> { 391 assertSame(k, EXTRA_KEY); 392 assertNull(v); 393 return null; 394 }), null, description); 395 assertFalse(map.containsKey(EXTRA_KEY), description + ": null key present"); 396 // ensure removal. 397 assertNull(map.put(EXTRA_KEY, EXTRA_VALUE)); 398 assertSame(map.compute(EXTRA_KEY, (k, v) -> { 399 assertSame(k, EXTRA_KEY); 400 assertSame(v, EXTRA_VALUE); 401 return null; 402 }), null, description + ": null resulted expected"); 403 assertFalse(map.containsKey(EXTRA_KEY), description + ": null key present"); 404 // compute with map containing null value 405 assertNull(map.put(EXTRA_KEY, null), description + ": unexpected value"); 406 assertSame(map.compute(EXTRA_KEY, (k, v) -> { 407 assertSame(k, EXTRA_KEY); 408 assertNull(v); 409 return null; 410 }), null, description); 411 assertFalse(map.containsKey(EXTRA_KEY), description + ": null key present"); 412 assertNull(map.put(EXTRA_KEY, null), description + ": unexpected value"); 413 assertSame(map.compute(EXTRA_KEY, (k, v) -> { 414 assertSame(k, EXTRA_KEY); 415 assertNull(v); 416 return EXTRA_VALUE; 417 }), EXTRA_VALUE, description); 418 assertTrue(map.containsKey(EXTRA_KEY), "null key present"); 419 } 420 421 @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all") 422 public void testCompute(String description, Map<IntegerEnum, String> map) { 423 assertTrue(map.containsKey(KEYS[1])); 424 Object value = map.get(KEYS[1]); 425 assertTrue(null == value || value == VALUES[1], description + String.valueOf(value)); 426 assertSame(map.compute(KEYS[1], (k, v) -> { 427 assertSame(k, KEYS[1]); 428 assertSame(v, value); 429 return EXTRA_VALUE; 430 }), EXTRA_VALUE, description); 431 assertSame(map.get(KEYS[1]), EXTRA_VALUE, description); 432 assertNull(map.compute(KEYS[1], (k, v) -> { 433 assertSame(v, EXTRA_VALUE); 434 return null; 435 }), description); 436 assertFalse(map.containsKey(KEYS[1])); 437 438 assertFalse(map.containsKey(EXTRA_KEY)); 439 assertSame(map.compute(EXTRA_KEY, (k, v) -> { 440 assertNull(v); 441 return EXTRA_VALUE; 442 }), EXTRA_VALUE); 443 assertTrue(map.containsKey(EXTRA_KEY)); 444 assertSame(map.get(EXTRA_KEY), EXTRA_VALUE); 445 } 446 447 @Test(expectedExceptions = {NullPointerException.class}) 448 public void testComputeNPEHashMap() { 449 Object value = new HashMap().compute(KEYS[1], null); 450 } 451 452 @Test(expectedExceptions = {NullPointerException.class}) 453 public void testComputeNPEHashtable() { 454 Object value = new Hashtable().compute(KEYS[1], null); 455 } 456 457 @Test(expectedExceptions = {NullPointerException.class}) 458 public void testComputeNPETreeMap() { 459 Object value = new TreeMap().compute(KEYS[1], null); 460 } 461 462 @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull") 463 public void testMergeNulls(String description, Map<IntegerEnum, String> map) { 464 assertTrue(map.containsKey(null), "null key absent"); 465 assertNull(map.get(null), "value not null"); 466 assertSame(map.merge(null, EXTRA_VALUE, (v, vv) -> { 467 assertNull(v); 468 assertSame(vv, EXTRA_VALUE); 469 return vv; 470 }), EXTRA_VALUE, description); 471 assertTrue(map.containsKey(null)); 472 assertSame(map.get(null), EXTRA_VALUE, description); 473 } 474 475 @Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all") 476 public void testMerge(String description, Map<IntegerEnum, String> map) { 477 assertTrue(map.containsKey(KEYS[1])); 478 Object value = map.get(KEYS[1]); 479 assertTrue(null == value || value == VALUES[1], description + String.valueOf(value)); 480 assertSame(map.merge(KEYS[1], EXTRA_VALUE, (v, vv) -> { 481 assertSame(v, value); 482 assertSame(vv, EXTRA_VALUE); 483 return vv; 484 }), EXTRA_VALUE, description); 485 assertSame(map.get(KEYS[1]), EXTRA_VALUE, description); 486 assertNull(map.merge(KEYS[1], EXTRA_VALUE, (v, vv) -> { 487 assertSame(v, EXTRA_VALUE); 488 assertSame(vv, EXTRA_VALUE); 489 return null; 490 }), description); 491 assertFalse(map.containsKey(KEYS[1])); 492 493 assertFalse(map.containsKey(EXTRA_KEY)); 494 assertSame(map.merge(EXTRA_KEY, EXTRA_VALUE, (v, vv) -> { 495 assertNull(v); 496 assertSame(vv, EXTRA_VALUE); 497 return EXTRA_VALUE; 498 }), EXTRA_VALUE); 499 assertTrue(map.containsKey(EXTRA_KEY)); 500 assertSame(map.get(EXTRA_KEY), EXTRA_VALUE); 501 } 502 503 @Test(expectedExceptions = {NullPointerException.class}) 504 public void testMergeNPEHashMap() { 505 Object value = new HashMap().merge(KEYS[1], VALUES[1], null); 506 } 507 508 @Test(expectedExceptions = {NullPointerException.class}) 509 public void testMergeNPEHashtable() { 510 Object value = new Hashtable().merge(KEYS[1], VALUES[1], null); 511 } 512 513 @Test(expectedExceptions = {NullPointerException.class}) 514 public void testMergeNPETreeMap() { 515 Object value = new TreeMap().merge(KEYS[1], VALUES[1], null); 516 } 517 518 enum IntegerEnum { 519 520 e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, 521 e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, 522 e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, 523 e30, e31, e32, e33, e34, e35, e36, e37, e38, e39, 524 e40, e41, e42, e43, e44, e45, e46, e47, e48, e49, 525 e50, e51, e52, e53, e54, e55, e56, e57, e58, e59, 526 e60, e61, e62, e63, e64, e65, e66, e67, e68, e69, 527 e70, e71, e72, e73, e74, e75, e76, e77, e78, e79, 528 e80, e81, e82, e83, e84, e85, e86, e87, e88, e89, 529 e90, e91, e92, e93, e94, e95, e96, e97, e98, e99, 530 EXTRA_KEY; 531 public static final int SIZE = values().length; 532 }; 533 private static final int TEST_SIZE = IntegerEnum.SIZE - 1; 534 /** 535 * Realized keys ensure that there is always a hard ref to all test objects. 536 */ 537 private static final IntegerEnum[] KEYS = new IntegerEnum[TEST_SIZE]; 538 /** 539 * Realized values ensure that there is always a hard ref to all test 540 * objects. 541 */ 542 private static final String[] VALUES = new String[TEST_SIZE]; 543 544 static { 545 IntegerEnum[] keys = IntegerEnum.values(); 546 for (int each = 0; each < TEST_SIZE; each++) { 547 KEYS[each] = keys[each]; 548 VALUES[each] = String.valueOf(each); 549 } 550 } 551 552 private static final IntegerEnum FIRST_KEY = KEYS[0]; 553 private static final String FIRST_VALUE = VALUES[0]; 554 private static final IntegerEnum EXTRA_KEY = IntegerEnum.EXTRA_KEY; 555 private static final String EXTRA_VALUE = String.valueOf(TEST_SIZE); 556 557 @DataProvider(name = "Map<IntegerEnum,String> rw=all keys=all values=all", parallel = true) 558 public static Iterator<Object[]> allMapProvider() { 559 return makeAllMaps().iterator(); 560 } 561 562 @DataProvider(name = "Map<IntegerEnum,String> rw=all keys=withNull values=withNull", parallel = true) 563 public static Iterator<Object[]> allMapWithNullsProvider() { 564 return makeAllMapsWithNulls().iterator(); 565 } 566 567 @DataProvider(name = "Map<IntegerEnum,String> rw=true keys=nonNull values=nonNull", parallel = true) 568 public static Iterator<Object[]> rwNonNullMapProvider() { 569 return makeRWNoNullsMaps().iterator(); 570 } 571 572 @DataProvider(name = "Map<IntegerEnum,String> rw=true keys=nonNull values=all", parallel = true) 573 public static Iterator<Object[]> rwNonNullKeysMapProvider() { 574 return makeRWMapsNoNulls().iterator(); 575 } 576 577 @DataProvider(name = "Map<IntegerEnum,String> rw=true keys=all values=all", parallel = true) 578 public static Iterator<Object[]> rwMapProvider() { 579 return makeAllRWMaps().iterator(); 580 } 581 582 @DataProvider(name = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull", parallel = true) 583 public static Iterator<Object[]> rwNullsMapProvider() { 584 return makeAllRWMapsWithNulls().iterator(); 585 } 586 587 private static Collection<Object[]> makeAllRWMapsWithNulls() { 588 Collection<Object[]> all = new ArrayList<>(); 589 590 all.addAll(makeRWMaps(true, true)); 591 592 return all; 593 } 594 595 596 private static Collection<Object[]> makeRWMapsNoNulls() { 597 Collection<Object[]> all = new ArrayList<>(); 598 599 all.addAll(makeRWNoNullKeysMaps(false)); 600 all.addAll(makeRWNoNullsMaps()); 601 602 return all; 603 } 604 605 private static Collection<Object[]> makeAllROMaps() { 606 Collection<Object[]> all = new ArrayList<>(); 607 608 all.addAll(makeROMaps(false)); 609 all.addAll(makeROMaps(true)); 610 611 return all; 612 } 613 614 private static Collection<Object[]> makeAllRWMaps() { 615 Collection<Object[]> all = new ArrayList<>(); 616 617 all.addAll(makeRWNoNullsMaps()); 618 all.addAll(makeRWMaps(false,true)); 619 all.addAll(makeRWMaps(true,true)); 620 all.addAll(makeRWNoNullKeysMaps(true)); 621 return all; 622 } 623 624 private static Collection<Object[]> makeAllMaps() { 625 Collection<Object[]> all = new ArrayList<>(); 626 627 all.addAll(makeAllROMaps()); 628 all.addAll(makeAllRWMaps()); 629 630 return all; 631 } 632 633 private static Collection<Object[]> makeAllMapsWithNulls() { 634 Collection<Object[]> all = new ArrayList<>(); 635 636 all.addAll(makeROMaps(true)); 637 all.addAll(makeRWMaps(true,true)); 638 639 return all; 640 } 641 /** 642 * 643 * @param nullKeys include null keys 644 * @param nullValues include null values 645 * @return 646 */ 647 private static Collection<Object[]> makeRWMaps(boolean nullKeys, boolean nullValues) { 648 return Arrays.asList( 649 new Object[]{"HashMap", makeMap(HashMap::new, nullKeys, nullValues)}, 650 new Object[]{"IdentityHashMap", makeMap(IdentityHashMap::new, nullKeys, nullValues)}, 651 new Object[]{"LinkedHashMap", makeMap(LinkedHashMap::new, nullKeys, nullValues)}, 652 new Object[]{"WeakHashMap", makeMap(WeakHashMap::new, nullKeys, nullValues)}, 653 new Object[]{"Collections.checkedMap(HashMap)", Collections.checkedMap(makeMap(HashMap::new, nullKeys, nullValues), IntegerEnum.class, String.class)}, 654 new Object[]{"Collections.synchronizedMap(HashMap)", Collections.synchronizedMap(makeMap(HashMap::new, nullKeys, nullValues))}, 655 new Object[]{"ExtendsAbstractMap", makeMap(ExtendsAbstractMap::new, nullKeys, nullValues)}); 656 } 657 658 /** 659 * 660 * @param nulls include null values 661 * @return 662 */ 663 private static Collection<Object[]> makeRWNoNullKeysMaps(boolean nulls) { 664 return Arrays.asList( 665 // null key hostile 666 new Object[]{"EnumMap", makeMap(() -> new EnumMap(IntegerEnum.class), false, nulls)}, 667 new Object[]{"TreeMap", makeMap(TreeMap::new, false, nulls)}, 668 new Object[]{"ExtendsAbstractMap(TreeMap)", makeMap(() -> {return new ExtendsAbstractMap(new TreeMap());}, false, nulls)}, 669 new Object[]{"Collections.synchronizedMap(EnumMap)", Collections.synchronizedMap(makeMap(() -> new EnumMap(IntegerEnum.class), false, nulls))} 670 ); 671 } 672 673 private static Collection<Object[]> makeRWNoNullsMaps() { 674 return Arrays.asList( 675 // null key and value hostile 676 new Object[]{"Hashtable", makeMap(Hashtable::new, false, false)}, 677 new Object[]{"ConcurrentHashMap", makeMap(ConcurrentHashMap::new, false, false)}, 678 new Object[]{"ConcurrentSkipListMap", makeMap(ConcurrentSkipListMap::new, false, false)}, 679 new Object[]{"Collections.synchronizedMap(ConcurrentHashMap)", Collections.synchronizedMap(makeMap(ConcurrentHashMap::new, false, false))}, 680 new Object[]{"Collections.checkedMap(ConcurrentHashMap)", Collections.checkedMap(makeMap(ConcurrentHashMap::new, false, false), IntegerEnum.class, String.class)}, 681 new Object[]{"ExtendsAbstractMap(ConcurrentHashMap)", makeMap(() -> {return new ExtendsAbstractMap(new ConcurrentHashMap());}, false, false)}, 682 new Object[]{"ImplementsConcurrentMap", makeMap(ImplementsConcurrentMap::new, false, false)} 683 ); 684 } 685 686 /** 687 * 688 * @param nulls include nulls 689 * @return 690 */ 691 private static Collection<Object[]> makeROMaps(boolean nulls) { 692 return Arrays.asList(new Object[][]{ 693 new Object[]{"Collections.unmodifiableMap(HashMap)", Collections.unmodifiableMap(makeMap(HashMap::new, nulls, nulls))} 694 }); 695 } 696 697 /** 698 * 699 * @param supplier a supplier of mutable map instances. 700 * 701 * @param nullKeys include null keys 702 * @param nullValues include null values 703 * @return 704 */ 705 private static Map<IntegerEnum, String> makeMap(Supplier<Map<IntegerEnum, String>> supplier, boolean nullKeys, boolean nullValues) { 706 Map<IntegerEnum, String> result = supplier.get(); 707 708 for (int each = 0; each < TEST_SIZE; each++) { 709 IntegerEnum key = nullKeys ? (each == 0) ? null : KEYS[each] : KEYS[each]; 710 String value = nullValues ? (each == 0) ? null : VALUES[each] : VALUES[each]; 711 712 result.put(key, value); 713 } 714 715 return result; 716 } 717 718 public interface Thrower<T extends Throwable> { 719 720 public void run() throws T; 721 } 722 723 public static <T extends Throwable> void assertThrows(Thrower<T> thrower, Class<T> throwable) { 724 assertThrows(thrower, throwable, null); 725 } 726 727 public static <T extends Throwable> void assertThrows(Thrower<T> thrower, Class<T> throwable, String message) { 728 Throwable thrown; 729 try { 730 thrower.run(); 731 thrown = null; 732 } catch (Throwable caught) { 733 thrown = caught; 734 } 735 736 assertInstance(thrown, throwable, 737 ((null != message) ? message : "") + 738 " Failed to throw " + throwable.getCanonicalName()); 739 } 740 741 public static <T extends Throwable> void assertThrows(Class<T> throwable, String message, Thrower<T>... throwers) { 742 for(Thrower<T> thrower : throwers) { 743 assertThrows(thrower, throwable, message); 744 } 745 } 746 747 public static void assertInstance(Object actual, Class<?> expected) { 748 assertInstance(expected.isInstance(actual), null); 749 } 750 751 public static void assertInstance(Object actual, Class<?> expected, String message) { 752 assertTrue(expected.isInstance(actual), message); 753 } 754 755 /** 756 * A simple mutable map implementation that provides only default 757 * implementations of all methods. ie. none of the Map interface default 758 * methods have overridden implementations. 759 * 760 * @param <K> Type of keys 761 * @param <V> Type of values 762 */ 763 public static class ExtendsAbstractMap<M extends Map<K,V>, K, V> extends AbstractMap<K, V> { 764 765 protected final M map; 766 767 public ExtendsAbstractMap() { this( (M) new HashMap<K,V>()); } 768 769 protected ExtendsAbstractMap(M map) { this.map = map; } 770 771 public Set<Map.Entry<K, V>> entrySet() { 772 return new AbstractSet<Map.Entry<K, V>>() { 773 public int size() { 774 return map.size(); 775 } 776 777 public Iterator<Map.Entry<K,V>> iterator() { 778 final Iterator<Map.Entry<K,V>> source = map.entrySet().iterator(); 779 return new Iterator<Map.Entry<K,V>>() { 780 public boolean hasNext() { return source.hasNext(); } 781 public Map.Entry<K,V> next() { return source.next(); } 782 public void remove() { source.remove(); } 783 }; 784 } 785 786 public boolean add(Map.Entry<K,V> e) { 787 return map.entrySet().add(e); 788 } 789 }; 790 } 791 792 public V put(K key, V value) { 793 return map.put(key, value); 794 } 795 } 796 797 /** 798 * A simple mutable concurrent map implementation that provides only default 799 * implementations of all methods. ie. none of the ConcurrentMap interface 800 * default methods have overridden implementations. 801 * 802 * @param <K> Type of keys 803 * @param <V> Type of values 804 */ 805 public static class ImplementsConcurrentMap<K, V> extends ExtendsAbstractMap<ConcurrentMap<K,V>, K, V> implements ConcurrentMap<K,V> { 806 public ImplementsConcurrentMap() { super(new ConcurrentHashMap<K,V>()); } 807 808 // ConcurrentMap reabstracts these methods 809 810 public V replace(K k, V v) { return map.replace(k, v); }; 811 812 public boolean replace(K k, V v, V vv) { return map.replace(k, v, vv); }; 813 814 public boolean remove(Object k, Object v) { return map.remove(k, v); } 815 816 public V putIfAbsent(K k, V v) { return map.putIfAbsent(k, v); } 817 } 818 }