ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/test/jtreg/util/Map/Defaults.java
Revision: 1.6
Committed: Mon Jan 8 03:12:03 2018 UTC (6 years, 4 months ago) by jsr166
Branch: MAIN
CVS Tags: HEAD
Changes since 1.5: +10 -10 lines
Log Message:
organize imports

File Contents

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