ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/test/tck/MapTest.java
(Generate patch)

Comparing jsr166/src/test/tck/MapTest.java (file contents):
Revision 1.8 by jsr166, Mon Jul 8 21:18:36 2019 UTC vs.
Revision 1.11 by dl, Tue Jan 26 13:33:06 2021 UTC

# Line 5 | Line 5
5   * http://creativecommons.org/publicdomain/zero/1.0/
6   */
7  
8 import junit.framework.Test;
9
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 + @SuppressWarnings("unchecked")
24   public class MapTest extends JSR166TestCase {
25      final MapImplementation impl;
26  
# Line 37 | Line 42 | public class MapTest extends JSR166TestC
42          {
43              Map m = impl.emptyMap();
44              assertTrue(m.isEmpty());
45 <            assertEquals(0, m.size());
45 >            mustEqual(0, m.size());
46              Object k = impl.makeKey(rnd.nextInt());
47              Object v = impl.makeValue(rnd.nextInt());
48              m.put(k, v);
49              assertFalse(m.isEmpty());
50 <            assertEquals(1, m.size());
50 >            mustEqual(1, m.size());
51              assertTrue(m.containsKey(k));
52              assertTrue(m.containsValue(v));
53          }
# Line 137 | Line 142 | public class MapTest extends JSR166TestC
142          assertFalse(m.containsValue(v1));
143          assertTrue(m.containsValue(v2));
144          assertTrue(m.containsKey(keyToFrob));
145 <        assertEquals(1, m.size());
145 >        mustEqual(1, m.size());
146      }
147  
148      /**
# Line 168 | Line 173 | public class MapTest extends JSR166TestC
173          m1.putAll(m2);
174  
175          for (Object elt : m2.keySet())
176 <            assertEquals(m2.get(elt), m1.get(elt));
176 >            mustEqual(m2.get(elt), m1.get(elt));
177          for (Object elt : m1Copy.keySet())
178              assertSame(m1Copy.get(elt), m1.get(elt));
179 <        assertEquals(size1 + size2, m1.size());
179 >        mustEqual(size1 + size2, m1.size());
180      }
181  
182      /**
# Line 186 | Line 191 | public class MapTest extends JSR166TestC
191          final Map clone = cloneableClone(map);
192          if (clone == null) return;      // not cloneable?
193  
194 <        assertEquals(size, map.size());
195 <        assertEquals(size, clone.size());
196 <        assertEquals(map.isEmpty(), clone.isEmpty());
194 >        mustEqual(size, map.size());
195 >        mustEqual(size, clone.size());
196 >        mustEqual(map.isEmpty(), clone.isEmpty());
197  
198          clone.put(impl.makeKey(-1), impl.makeValue(-1));
199 <        assertEquals(size, map.size());
200 <        assertEquals(size + 1, clone.size());
199 >        mustEqual(size, map.size());
200 >        mustEqual(size + 1, clone.size());
201  
202          clone.clear();
203 <        assertEquals(size, map.size());
204 <        assertEquals(0, clone.size());
203 >        mustEqual(size, map.size());
204 >        mustEqual(0, clone.size());
205          assertTrue(clone.isEmpty());
206      }
207  
208 +    /**
209 +     * Concurrent access by compute methods behaves as expected
210 +     */
211 +    public void testConcurrentAccess() throws Throwable {
212 +        final Map map = impl.emptyMap();
213 +        final long testDurationMillis = expensiveTests ? 1000 : 2;
214 +        final int nTasks = impl.isConcurrent()
215 +            ? ThreadLocalRandom.current().nextInt(1, 10)
216 +            : 1;
217 +        final AtomicBoolean done = new AtomicBoolean(false);
218 +        final boolean remappingFunctionCalledAtMostOnce
219 +            = impl.remappingFunctionCalledAtMostOnce();
220 +        final List<CompletableFuture> futures = new ArrayList<>();
221 +        final AtomicLong expectedSum = new AtomicLong(0);
222 +        final Action[] tasks = {
223 +            // repeatedly increment values using compute()
224 +            () -> {
225 +                long[] invocations = new long[2];
226 +                ThreadLocalRandom rnd = ThreadLocalRandom.current();
227 +                BiFunction<Object, Object, Object> incValue = (k, v) -> {
228 +                    invocations[1]++;
229 +                    int vi = (v == null) ? 1 : impl.valueToInt(v) + 1;
230 +                    return impl.makeValue(vi);
231 +                };
232 +                while (!done.getAcquire()) {
233 +                    invocations[0]++;
234 +                    Object key = impl.makeKey(3 * rnd.nextInt(10));
235 +                    map.compute(key, incValue);
236 +                }
237 +                if (remappingFunctionCalledAtMostOnce)
238 +                    mustEqual(invocations[0], invocations[1]);
239 +                expectedSum.getAndAdd(invocations[0]);
240 +            },
241 +            // repeatedly increment values using computeIfPresent()
242 +            () -> {
243 +                long[] invocations = new long[2];
244 +                ThreadLocalRandom rnd = ThreadLocalRandom.current();
245 +                BiFunction<Object, Object, Object> incValue = (k, v) -> {
246 +                    invocations[1]++;
247 +                    int vi = impl.valueToInt(v) + 1;
248 +                    return impl.makeValue(vi);
249 +                };
250 +                while (!done.getAcquire()) {
251 +                    Object key = impl.makeKey(3 * rnd.nextInt(10));
252 +                    if (map.computeIfPresent(key, incValue) != null)
253 +                        invocations[0]++;
254 +                }
255 +                if (remappingFunctionCalledAtMostOnce)
256 +                    mustEqual(invocations[0], invocations[1]);
257 +                expectedSum.getAndAdd(invocations[0]);
258 +            },
259 +        };
260 +        for (int i = nTasks; i--> 0; ) {
261 +            Action task = chooseRandomly(tasks);
262 +            futures.add(CompletableFuture.runAsync(checkedRunnable(task)));
263 +        }
264 +        Thread.sleep(testDurationMillis);
265 +        done.setRelease(true);
266 +        for (var future : futures)
267 +            checkTimedGet(future, null);
268 +
269 +        long sum = map.values().stream().mapToLong(x -> (int) x).sum();
270 +        mustEqual(expectedSum.get(), sum);
271 +    }
272 +
273   //     public void testFailsIntentionallyForDebugging() {
274   //         fail(impl.klazz().getSimpleName());
275   //     }

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines