ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/test/tck/MapTest.java
Revision: 1.9
Committed: Sun Sep 29 20:40:48 2019 UTC (4 years, 7 months ago) by jsr166
Branch: MAIN
Changes since 1.8: +72 -1 lines
Log Message:
add MapTest.testConcurrentAccess

File Contents

# User Rev Content
1 jsr166 1.1 /*
2     * Written by Doug Lea and Martin Buchholz with assistance from
3     * members of JCP JSR-166 Expert Group and released to the public
4     * domain, as explained at
5     * http://creativecommons.org/publicdomain/zero/1.0/
6     */
7    
8 jsr166 1.9 import static java.util.concurrent.TimeUnit.MILLISECONDS;
9 jsr166 1.1
10     import java.util.ArrayList;
11     import java.util.Iterator;
12     import java.util.List;
13     import java.util.Map;
14 jsr166 1.9 import java.util.concurrent.CompletableFuture;
15 jsr166 1.1 import java.util.concurrent.ThreadLocalRandom;
16 jsr166 1.9 import java.util.concurrent.atomic.AtomicBoolean;
17     import java.util.concurrent.atomic.AtomicLong;
18     import java.util.function.BiFunction;
19    
20     import junit.framework.Test;
21 jsr166 1.1
22     /**
23     * Contains tests applicable to all Map implementations.
24     */
25     public class MapTest extends JSR166TestCase {
26     final MapImplementation impl;
27    
28     /** Tests are parameterized by a Map implementation. */
29     MapTest(MapImplementation impl, String methodName) {
30     super(methodName);
31     this.impl = impl;
32     }
33    
34     public static Test testSuite(MapImplementation impl) {
35     return newTestSuite(
36     parameterizedTestSuite(MapTest.class,
37     MapImplementation.class,
38     impl));
39     }
40    
41     public void testImplSanity() {
42     final ThreadLocalRandom rnd = ThreadLocalRandom.current();
43     {
44     Map m = impl.emptyMap();
45     assertTrue(m.isEmpty());
46     assertEquals(0, m.size());
47     Object k = impl.makeKey(rnd.nextInt());
48     Object v = impl.makeValue(rnd.nextInt());
49     m.put(k, v);
50     assertFalse(m.isEmpty());
51     assertEquals(1, m.size());
52     assertTrue(m.containsKey(k));
53     assertTrue(m.containsValue(v));
54     }
55     {
56     Map m = impl.emptyMap();
57     Object v = impl.makeValue(rnd.nextInt());
58     if (impl.permitsNullKeys()) {
59     m.put(null, v);
60     assertTrue(m.containsKey(null));
61     assertTrue(m.containsValue(v));
62     } else {
63     assertThrows(NullPointerException.class, () -> m.put(null, v));
64     }
65     }
66     {
67     Map m = impl.emptyMap();
68     Object k = impl.makeKey(rnd.nextInt());
69     if (impl.permitsNullValues()) {
70     m.put(k, null);
71     assertTrue(m.containsKey(k));
72     assertTrue(m.containsValue(null));
73     } else {
74     assertThrows(NullPointerException.class, () -> m.put(k, null));
75     }
76     }
77     {
78     Map m = impl.emptyMap();
79     Object k = impl.makeKey(rnd.nextInt());
80     Object v1 = impl.makeValue(rnd.nextInt());
81     Object v2 = impl.makeValue(rnd.nextInt());
82     m.put(k, v1);
83     if (impl.supportsSetValue()) {
84     ((Map.Entry)(m.entrySet().iterator().next())).setValue(v2);
85     assertSame(v2, m.get(k));
86     assertTrue(m.containsKey(k));
87     assertTrue(m.containsValue(v2));
88     assertFalse(m.containsValue(v1));
89     } else {
90     assertThrows(UnsupportedOperationException.class,
91     () -> ((Map.Entry)(m.entrySet().iterator().next())).setValue(v2));
92     }
93     }
94     }
95    
96     /**
97     * Tests and extends the scenario reported in
98     * https://bugs.openjdk.java.net/browse/JDK-8186171
99     * HashMap: Entry.setValue may not work after Iterator.remove() called for previous entries
100     * ant -Djsr166.tckTestClass=HashMapTest -Djsr166.methodFilter=testBug8186171 -Djsr166.runsPerTest=1000 tck
101     */
102     public void testBug8186171() {
103     if (!impl.supportsSetValue()) return;
104 jsr166 1.8 if (!atLeastJava10()) return; // jdk9 is no longer maintained
105 jsr166 1.1 final ThreadLocalRandom rnd = ThreadLocalRandom.current();
106     final boolean permitsNullValues = impl.permitsNullValues();
107     final Object v1 = (permitsNullValues && rnd.nextBoolean())
108     ? null : impl.makeValue(1);
109     final Object v2 = (permitsNullValues && rnd.nextBoolean() && v1 != null)
110     ? null : impl.makeValue(2);
111    
112 jsr166 1.2 // If true, always lands in first bucket in hash tables.
113 jsr166 1.1 final boolean poorHash = rnd.nextBoolean();
114     class Key implements Comparable<Key> {
115     final int i;
116     Key(int i) { this.i = i; }
117     public int hashCode() { return poorHash ? 0 : super.hashCode(); }
118     public int compareTo(Key x) {
119     return Integer.compare(this.i, x.i);
120     }
121     }
122    
123     // Both HashMap and ConcurrentHashMap have:
124     // TREEIFY_THRESHOLD = 8; UNTREEIFY_THRESHOLD = 6;
125     final int size = rnd.nextInt(1, 25);
126    
127     List<Key> keys = new ArrayList<>();
128     for (int i = size; i-->0; ) keys.add(new Key(i));
129     Key keyToFrob = keys.get(rnd.nextInt(keys.size()));
130    
131     Map<Key, Object> m = impl.emptyMap();
132     for (Key key : keys) m.put(key, v1);
133    
134     for (Iterator<Map.Entry<Key, Object>> it = m.entrySet().iterator();
135     it.hasNext(); ) {
136     Map.Entry<Key, Object> entry = it.next();
137     if (entry.getKey() == keyToFrob)
138     entry.setValue(v2); // does this have the expected effect?
139     else
140     it.remove();
141     }
142    
143     assertFalse(m.containsValue(v1));
144     assertTrue(m.containsValue(v2));
145     assertTrue(m.containsKey(keyToFrob));
146     assertEquals(1, m.size());
147     }
148    
149 jsr166 1.3 /**
150     * "Missing" test found while investigating JDK-8210280.
151 jsr166 1.4 * ant -Djsr166.tckTestClass=HashMapTest -Djsr166.methodFilter=testBug8210280 -Djsr166.runsPerTest=1000000 tck
152 jsr166 1.3 */
153     public void testBug8210280() {
154 jsr166 1.4 final ThreadLocalRandom rnd = ThreadLocalRandom.current();
155     final int size1 = rnd.nextInt(32);
156     final int size2 = rnd.nextInt(128);
157    
158     final Map m1 = impl.emptyMap();
159     for (int i = 0; i < size1; i++) {
160     int elt = rnd.nextInt(1024 * i, 1024 * (i + 1));
161     assertNull(m1.put(impl.makeKey(elt), impl.makeValue(elt)));
162     }
163    
164     final Map m2 = impl.emptyMap();
165     for (int i = 0; i < size2; i++) {
166     int elt = rnd.nextInt(Integer.MIN_VALUE + 1024 * i,
167     Integer.MIN_VALUE + 1024 * (i + 1));
168     assertNull(m2.put(impl.makeKey(elt), impl.makeValue(-elt)));
169     }
170    
171     final Map m1Copy = impl.emptyMap();
172     m1Copy.putAll(m1);
173    
174     m1.putAll(m2);
175    
176     for (Object elt : m2.keySet())
177     assertEquals(m2.get(elt), m1.get(elt));
178     for (Object elt : m1Copy.keySet())
179     assertSame(m1Copy.get(elt), m1.get(elt));
180     assertEquals(size1 + size2, m1.size());
181 jsr166 1.3 }
182    
183 jsr166 1.5 /**
184     * 8222930: ConcurrentSkipListMap.clone() shares size variable between original and clone
185     */
186     public void testClone() {
187     final ThreadLocalRandom rnd = ThreadLocalRandom.current();
188     final int size = rnd.nextInt(4);
189     final Map map = impl.emptyMap();
190 jsr166 1.6 for (int i = 0; i < size; i++)
191 jsr166 1.5 map.put(impl.makeKey(i), impl.makeValue(i));
192     final Map clone = cloneableClone(map);
193     if (clone == null) return; // not cloneable?
194    
195     assertEquals(size, map.size());
196     assertEquals(size, clone.size());
197     assertEquals(map.isEmpty(), clone.isEmpty());
198    
199     clone.put(impl.makeKey(-1), impl.makeValue(-1));
200     assertEquals(size, map.size());
201     assertEquals(size + 1, clone.size());
202    
203     clone.clear();
204     assertEquals(size, map.size());
205     assertEquals(0, clone.size());
206     assertTrue(clone.isEmpty());
207     }
208    
209 jsr166 1.9 /**
210     * Concurrent access by compute methods behaves as expected
211     */
212     public void testConcurrentAccess() throws Throwable {
213     final Map map = impl.emptyMap();
214     final long testDurationMillis = expensiveTests ? 1000 : 2;
215     final int nTasks = impl.isConcurrent()
216     ? ThreadLocalRandom.current().nextInt(1, 10)
217     : 1;
218     final AtomicBoolean done = new AtomicBoolean(false);
219     final boolean remappingFunctionCalledAtMostOnce
220     = impl.remappingFunctionCalledAtMostOnce();
221     final List<CompletableFuture> futures = new ArrayList<>();
222     final AtomicLong expectedSum = new AtomicLong(0);
223     final Action[] tasks = {
224     // repeatedly increment values using compute()
225     () -> {
226     long[] invocations = new long[2];
227     ThreadLocalRandom rnd = ThreadLocalRandom.current();
228     BiFunction<Object, Object, Object> incValue = (k, v) -> {
229     invocations[1]++;
230     int vi = (v == null) ? 1 : impl.valueToInt(v) + 1;
231     return impl.makeValue(vi);
232     };
233     while (!done.getAcquire()) {
234     invocations[0]++;
235     Object key = impl.makeKey(3 * rnd.nextInt(10));
236     map.compute(key, incValue);
237     }
238     if (remappingFunctionCalledAtMostOnce)
239     assertEquals(invocations[0], invocations[1]);
240     expectedSum.getAndAdd(invocations[0]);
241     },
242     // repeatedly increment values using computeIfPresent()
243     () -> {
244     long[] invocations = new long[2];
245     ThreadLocalRandom rnd = ThreadLocalRandom.current();
246     BiFunction<Object, Object, Object> incValue = (k, v) -> {
247     invocations[1]++;
248     int vi = impl.valueToInt(v) + 1;
249     return impl.makeValue(vi);
250     };
251     while (!done.getAcquire()) {
252     Object key = impl.makeKey(3 * rnd.nextInt(10));
253     if (map.computeIfPresent(key, incValue) != null)
254     invocations[0]++;
255     }
256     if (remappingFunctionCalledAtMostOnce)
257     assertEquals(invocations[0], invocations[1]);
258     expectedSum.getAndAdd(invocations[0]);
259     },
260     };
261     for (int i = nTasks; i--> 0; ) {
262     Action task = chooseRandomly(tasks);
263     futures.add(CompletableFuture.runAsync(checkedRunnable(task)));
264     }
265     Thread.sleep(testDurationMillis);
266     done.setRelease(true);
267     for (var future : futures)
268     checkTimedGet(future, null);
269    
270     long sum = map.values().stream().mapToLong(x -> (int) x).sum();
271     assertEquals(expectedSum.get(), sum);
272     }
273    
274 jsr166 1.1 // public void testFailsIntentionallyForDebugging() {
275     // fail(impl.klazz().getSimpleName());
276     // }
277     }