ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/test/tck/MapTest.java
Revision: 1.1
Committed: Wed Aug 23 05:33:00 2017 UTC (6 years, 8 months ago) by jsr166
Branch: MAIN
Log Message:
8186171: HashMap: Entry.setValue may not work after Iterator.remove() called for previous entries

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     import junit.framework.Test;
9    
10     import java.util.ArrayList;
11     import java.util.Iterator;
12     import java.util.List;
13     import java.util.Map;
14     import java.util.concurrent.ThreadLocalRandom;
15    
16     /**
17     * Contains tests applicable to all Map implementations.
18     */
19     public class MapTest extends JSR166TestCase {
20     final MapImplementation impl;
21    
22     /** Tests are parameterized by a Map implementation. */
23     MapTest(MapImplementation impl, String methodName) {
24     super(methodName);
25     this.impl = impl;
26     }
27    
28     public static Test testSuite(MapImplementation impl) {
29     return newTestSuite(
30     parameterizedTestSuite(MapTest.class,
31     MapImplementation.class,
32     impl));
33     }
34    
35     public void testImplSanity() {
36     final ThreadLocalRandom rnd = ThreadLocalRandom.current();
37     {
38     Map m = impl.emptyMap();
39     assertTrue(m.isEmpty());
40     assertEquals(0, m.size());
41     Object k = impl.makeKey(rnd.nextInt());
42     Object v = impl.makeValue(rnd.nextInt());
43     m.put(k, v);
44     assertFalse(m.isEmpty());
45     assertEquals(1, m.size());
46     assertTrue(m.containsKey(k));
47     assertTrue(m.containsValue(v));
48     }
49     {
50     Map m = impl.emptyMap();
51     Object v = impl.makeValue(rnd.nextInt());
52     if (impl.permitsNullKeys()) {
53     m.put(null, v);
54     assertTrue(m.containsKey(null));
55     assertTrue(m.containsValue(v));
56     } else {
57     assertThrows(NullPointerException.class, () -> m.put(null, v));
58     }
59     }
60     {
61     Map m = impl.emptyMap();
62     Object k = impl.makeKey(rnd.nextInt());
63     if (impl.permitsNullValues()) {
64     m.put(k, null);
65     assertTrue(m.containsKey(k));
66     assertTrue(m.containsValue(null));
67     } else {
68     assertThrows(NullPointerException.class, () -> m.put(k, null));
69     }
70     }
71     {
72     Map m = impl.emptyMap();
73     Object k = impl.makeKey(rnd.nextInt());
74     Object v1 = impl.makeValue(rnd.nextInt());
75     Object v2 = impl.makeValue(rnd.nextInt());
76     m.put(k, v1);
77     if (impl.supportsSetValue()) {
78     ((Map.Entry)(m.entrySet().iterator().next())).setValue(v2);
79     assertSame(v2, m.get(k));
80     assertTrue(m.containsKey(k));
81     assertTrue(m.containsValue(v2));
82     assertFalse(m.containsValue(v1));
83     } else {
84     assertThrows(UnsupportedOperationException.class,
85     () -> ((Map.Entry)(m.entrySet().iterator().next())).setValue(v2));
86     }
87     }
88     }
89    
90     /**
91     * Tests and extends the scenario reported in
92     * https://bugs.openjdk.java.net/browse/JDK-8186171
93     * HashMap: Entry.setValue may not work after Iterator.remove() called for previous entries
94     * ant -Djsr166.tckTestClass=HashMapTest -Djsr166.methodFilter=testBug8186171 -Djsr166.runsPerTest=1000 tck
95     */
96     public void testBug8186171() {
97     if (!impl.supportsSetValue()) return;
98     final ThreadLocalRandom rnd = ThreadLocalRandom.current();
99     final boolean permitsNullValues = impl.permitsNullValues();
100     final Object v1 = (permitsNullValues && rnd.nextBoolean())
101     ? null : impl.makeValue(1);
102     final Object v2 = (permitsNullValues && rnd.nextBoolean() && v1 != null)
103     ? null : impl.makeValue(2);
104    
105     /** If true, always lands in first bucket in hash tables. */
106     final boolean poorHash = rnd.nextBoolean();
107     class Key implements Comparable<Key> {
108     final int i;
109     Key(int i) { this.i = i; }
110     public int hashCode() { return poorHash ? 0 : super.hashCode(); }
111     public int compareTo(Key x) {
112     return Integer.compare(this.i, x.i);
113     }
114     }
115    
116     // Both HashMap and ConcurrentHashMap have:
117     // TREEIFY_THRESHOLD = 8; UNTREEIFY_THRESHOLD = 6;
118     final int size = rnd.nextInt(1, 25);
119    
120     List<Key> keys = new ArrayList<>();
121     for (int i = size; i-->0; ) keys.add(new Key(i));
122     Key keyToFrob = keys.get(rnd.nextInt(keys.size()));
123    
124     Map<Key, Object> m = impl.emptyMap();
125     for (Key key : keys) m.put(key, v1);
126    
127     for (Iterator<Map.Entry<Key, Object>> it = m.entrySet().iterator();
128     it.hasNext(); ) {
129     Map.Entry<Key, Object> entry = it.next();
130     if (entry.getKey() == keyToFrob)
131     entry.setValue(v2); // does this have the expected effect?
132     else
133     it.remove();
134     }
135    
136     assertFalse(m.containsValue(v1));
137     assertTrue(m.containsValue(v2));
138     assertTrue(m.containsKey(keyToFrob));
139     assertEquals(1, m.size());
140     }
141    
142     // public void testFailsIntentionallyForDebugging() {
143     // fail(impl.klazz().getSimpleName());
144     // }
145     }