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 */
31import java.util.AbstractMap;
32import java.util.AbstractSet;
33import java.util.ArrayList;
34import java.util.Arrays;
35import java.util.Collection;
36import java.util.Collections;
37import java.util.EnumMap;
38import java.util.HashMap;
39import java.util.Hashtable;
40import java.util.HashSet;
41import java.util.IdentityHashMap;
42import java.util.Iterator;
43import java.util.LinkedHashMap;
44import java.util.Map;
45import java.util.TreeMap;
46import java.util.Set;
47import java.util.WeakHashMap;
48import java.util.concurrent.ConcurrentMap;
49import java.util.concurrent.ConcurrentHashMap;
50import java.util.concurrent.ConcurrentSkipListMap;
51import java.util.concurrent.atomic.AtomicBoolean;
52import java.util.function.BiFunction;
53import java.util.function.Function;
54import java.util.function.Supplier;
55
56import org.testng.Assert.ThrowingRunnable;
57import org.testng.annotations.Test;
58import org.testng.annotations.DataProvider;
59
60import static java.util.Objects.requireNonNull;
61
62import static org.testng.Assert.fail;
63import static org.testng.Assert.assertEquals;
64import static org.testng.Assert.assertTrue;
65import static org.testng.Assert.assertFalse;
66import static org.testng.Assert.assertNull;
67import static org.testng.Assert.assertSame;
68import static org.testng.Assert.assertThrows;
69
70public 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}
1051