ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/test/tck/MapTest.java
Revision: 1.10
Committed: Mon Oct 7 21:06:02 2019 UTC (4 years, 7 months ago) by jsr166
Branch: MAIN
Changes since 1.9: +0 -2 lines
Log Message:
fix imports

File Contents

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