1 /*
2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3  *
4  * This code is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License version 2 only, as
6  * published by the Free Software Foundation.
7  *
8  * This code is distributed in the hope that it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
11  * version 2 for more details (a copy is included in the LICENSE file that
12  * accompanied this code).
13  *
14  * You should have received a copy of the GNU General Public License version
15  * 2 along with this work; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
17  *
18  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
19  * or visit www.oracle.com if you need additional information or have any
20  * questions.
21  */
22 
23 /*
24  * This file is available under and governed by the GNU General Public
25  * License version 2 only, as published by the Free Software Foundation.
26  * However, the following notice accompanied the original version of this
27  * file:
28  *
29  * Written by Doug Lea and Martin Buchholz with assistance from
30  * members of JCP JSR-166 Expert Group and released to the public
31  * domain, as explained at
32  * http://creativecommons.org/publicdomain/zero/1.0/
33  */
34 
35 import junit.framework.Test;
36 
37 import java.util.ArrayList;
38 import java.util.Iterator;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.concurrent.ThreadLocalRandom;
42 
43 /**
44  * Contains tests applicable to all Map implementations.
45  */
46 public class MapTest extends JSR166TestCase {
47     final MapImplementation impl;
48 
49     /** Tests are parameterized by a Map implementation. */
MapTest(MapImplementation impl, String methodName)50     MapTest(MapImplementation impl, String methodName) {
51         super(methodName);
52         this.impl = impl;
53     }
54 
testSuite(MapImplementation impl)55     public static Test testSuite(MapImplementation impl) {
56         return newTestSuite(
57             parameterizedTestSuite(MapTest.class,
58                                    MapImplementation.class,
59                                    impl));
60     }
61 
testImplSanity()62     public void testImplSanity() {
63         final ThreadLocalRandom rnd = ThreadLocalRandom.current();
64         {
65             Map m = impl.emptyMap();
66             assertTrue(m.isEmpty());
67             assertEquals(0, m.size());
68             Object k = impl.makeKey(rnd.nextInt());
69             Object v = impl.makeValue(rnd.nextInt());
70             m.put(k, v);
71             assertFalse(m.isEmpty());
72             assertEquals(1, m.size());
73             assertTrue(m.containsKey(k));
74             assertTrue(m.containsValue(v));
75         }
76         {
77             Map m = impl.emptyMap();
78             Object v = impl.makeValue(rnd.nextInt());
79             if (impl.permitsNullKeys()) {
80                 m.put(null, v);
81                 assertTrue(m.containsKey(null));
82                 assertTrue(m.containsValue(v));
83             } else {
84                 assertThrows(NullPointerException.class, () -> m.put(null, v));
85             }
86         }
87         {
88             Map m = impl.emptyMap();
89             Object k = impl.makeKey(rnd.nextInt());
90             if (impl.permitsNullValues()) {
91                 m.put(k, null);
92                 assertTrue(m.containsKey(k));
93                 assertTrue(m.containsValue(null));
94             } else {
95                 assertThrows(NullPointerException.class, () -> m.put(k, null));
96             }
97         }
98         {
99             Map m = impl.emptyMap();
100             Object k = impl.makeKey(rnd.nextInt());
101             Object v1 = impl.makeValue(rnd.nextInt());
102             Object v2 = impl.makeValue(rnd.nextInt());
103             m.put(k, v1);
104             if (impl.supportsSetValue()) {
105                 ((Map.Entry)(m.entrySet().iterator().next())).setValue(v2);
106                 assertSame(v2, m.get(k));
107                 assertTrue(m.containsKey(k));
108                 assertTrue(m.containsValue(v2));
109                 assertFalse(m.containsValue(v1));
110             } else {
111                 assertThrows(UnsupportedOperationException.class,
112                              () -> ((Map.Entry)(m.entrySet().iterator().next())).setValue(v2));
113             }
114         }
115     }
116 
117     /**
118      * Tests and extends the scenario reported in
119      * https://bugs.openjdk.java.net/browse/JDK-8186171
120      * HashMap: Entry.setValue may not work after Iterator.remove() called for previous entries
121      * ant -Djsr166.tckTestClass=HashMapTest -Djsr166.methodFilter=testBug8186171 -Djsr166.runsPerTest=1000 tck
122      */
testBug8186171()123     public void testBug8186171() {
124         if (!impl.supportsSetValue()) return;
125         final ThreadLocalRandom rnd = ThreadLocalRandom.current();
126         final boolean permitsNullValues = impl.permitsNullValues();
127         final Object v1 = (permitsNullValues && rnd.nextBoolean())
128             ? null : impl.makeValue(1);
129         final Object v2 = (permitsNullValues && rnd.nextBoolean() && v1 != null)
130             ? null : impl.makeValue(2);
131 
132         // If true, always lands in first bucket in hash tables.
133         final boolean poorHash = rnd.nextBoolean();
134         class Key implements Comparable<Key> {
135             final int i;
136             Key(int i) { this.i = i; }
137             public int hashCode() { return poorHash ? 0 : super.hashCode(); }
138             public int compareTo(Key x) {
139                 return Integer.compare(this.i, x.i);
140             }
141         }
142 
143         // Both HashMap and ConcurrentHashMap have:
144         // TREEIFY_THRESHOLD = 8; UNTREEIFY_THRESHOLD = 6;
145         final int size = rnd.nextInt(1, 25);
146 
147         List<Key> keys = new ArrayList<>();
148         for (int i = size; i-->0; ) keys.add(new Key(i));
149         Key keyToFrob = keys.get(rnd.nextInt(keys.size()));
150 
151         Map<Key, Object> m = impl.emptyMap();
152         for (Key key : keys) m.put(key, v1);
153 
154         for (Iterator<Map.Entry<Key, Object>> it = m.entrySet().iterator();
155              it.hasNext(); ) {
156             Map.Entry<Key, Object> entry = it.next();
157             if (entry.getKey() == keyToFrob)
158                 entry.setValue(v2); // does this have the expected effect?
159             else
160                 it.remove();
161         }
162 
163         assertFalse(m.containsValue(v1));
164         assertTrue(m.containsValue(v2));
165         assertTrue(m.containsKey(keyToFrob));
166         assertEquals(1, m.size());
167     }
168 
169     /**
170      * "Missing" test found while investigating JDK-8210280.
171      * ant -Djsr166.tckTestClass=HashMapTest -Djsr166.methodFilter=testBug8210280 -Djsr166.runsPerTest=1000000 tck
172      */
testBug8210280()173     public void testBug8210280() {
174         final ThreadLocalRandom rnd = ThreadLocalRandom.current();
175         final int size1 = rnd.nextInt(32);
176         final int size2 = rnd.nextInt(128);
177 
178         final Map m1 = impl.emptyMap();
179         for (int i = 0; i < size1; i++) {
180             int elt = rnd.nextInt(1024 * i, 1024 * (i + 1));
181             assertNull(m1.put(impl.makeKey(elt), impl.makeValue(elt)));
182         }
183 
184         final Map m2 = impl.emptyMap();
185         for (int i = 0; i < size2; i++) {
186             int elt = rnd.nextInt(Integer.MIN_VALUE + 1024 * i,
187                                   Integer.MIN_VALUE + 1024 * (i + 1));
188             assertNull(m2.put(impl.makeKey(elt), impl.makeValue(-elt)));
189         }
190 
191         final Map m1Copy = impl.emptyMap();
192         m1Copy.putAll(m1);
193 
194         m1.putAll(m2);
195 
196         for (Object elt : m2.keySet())
197             assertEquals(m2.get(elt), m1.get(elt));
198         for (Object elt : m1Copy.keySet())
199             assertSame(m1Copy.get(elt), m1.get(elt));
200         assertEquals(size1 + size2, m1.size());
201     }
202 
203     /**
204      * 8222930: ConcurrentSkipListMap.clone() shares size variable between original and clone
205      */
testClone()206     public void testClone() {
207         final ThreadLocalRandom rnd = ThreadLocalRandom.current();
208         final int size = rnd.nextInt(4);
209         final Map map = impl.emptyMap();
210         for (int i = 0; i < size; i++)
211             map.put(impl.makeKey(i), impl.makeValue(i));
212         final Map clone = cloneableClone(map);
213         if (clone == null) return;      // not cloneable?
214 
215         assertEquals(size, map.size());
216         assertEquals(size, clone.size());
217         assertEquals(map.isEmpty(), clone.isEmpty());
218 
219         clone.put(impl.makeKey(-1), impl.makeValue(-1));
220         assertEquals(size, map.size());
221         assertEquals(size + 1, clone.size());
222 
223         clone.clear();
224         assertEquals(size, map.size());
225         assertEquals(0, clone.size());
226         assertTrue(clone.isEmpty());
227     }
228 
229 //     public void testFailsIntentionallyForDebugging() {
230 //         fail(impl.klazz().getSimpleName());
231 //     }
232 }
233