--- jsr166/src/test/tck/ConcurrentHashMapTest.java 2013/05/30 03:28:55 1.31 +++ jsr166/src/test/tck/ConcurrentHashMapTest.java 2015/02/22 04:34:44 1.42 @@ -6,10 +6,20 @@ * Pat Fisher, Mike Judd. */ -import junit.framework.*; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.Map; +import java.util.Random; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import junit.framework.Test; +import junit.framework.TestSuite; + public class ConcurrentHashMapTest extends JSR166TestCase { public static void main(String[] args) { junit.textui.TestRunner.run(suite()); @@ -21,8 +31,8 @@ public class ConcurrentHashMapTest exten /** * Returns a new map from Integers 1-5 to Strings "A"-"E". */ - private static ConcurrentHashMap map5() { - ConcurrentHashMap map = new ConcurrentHashMap(5); + private static ConcurrentHashMap map5() { + ConcurrentHashMap map = new ConcurrentHashMap(5); assertTrue(map.isEmpty()); map.put(one, "A"); map.put(two, "B"); @@ -34,12 +44,15 @@ public class ConcurrentHashMapTest exten return map; } + /** Re-implement Integer.compare for old java versions */ + static int compare(int x, int y) { return x < y ? -1 : x > y ? 1 : 0; } + // classes for testing Comparable fallbacks static class BI implements Comparable { private final int value; BI(int value) { this.value = value; } public int compareTo(BI other) { - return Integer.compare(value, other.value); + return compare(value, other.value); } public boolean equals(Object x) { return (x instanceof BI) && ((BI)x).value == value; @@ -73,22 +86,40 @@ public class ConcurrentHashMapTest exten break; } if (r == 0) - r = Integer.compare(size(), other.size()); + r = compare(size(), other.size()); return r; } private static final long serialVersionUID = 0; } + static class CollidingObject { + final String value; + CollidingObject(final String value) { this.value = value; } + public int hashCode() { return this.value.hashCode() & 1; } + public boolean equals(final Object obj) { + return (obj instanceof CollidingObject) && ((CollidingObject)obj).value.equals(value); + } + } + + static class ComparableCollidingObject extends CollidingObject implements Comparable { + ComparableCollidingObject(final String value) { super(value); } + public int compareTo(final ComparableCollidingObject o) { + return value.compareTo(o.value); + } + } + /** * Inserted elements that are subclasses of the same Comparable * class are found. */ public void testComparableFamily() { - ConcurrentHashMap m = new ConcurrentHashMap<>(); - for (int i = 0; i < 1000; i++) { + int size = 500; // makes measured test run time -> 60ms + ConcurrentHashMap m = + new ConcurrentHashMap(); + for (int i = 0; i < size; i++) { assertTrue(m.put(new CI(i), true) == null); } - for (int i = 0; i < 1000; i++) { + for (int i = 0; i < size; i++) { assertTrue(m.containsKey(new CI(i))); assertTrue(m.containsKey(new DI(i))); } @@ -99,8 +130,10 @@ public class ConcurrentHashMapTest exten * on Comparable can be inserted and found. */ public void testGenericComparable() { - ConcurrentHashMap m = new ConcurrentHashMap<>(); - for (int i = 0; i < 1000; i++) { + int size = 120; // makes measured test run time -> 60ms + ConcurrentHashMap m = + new ConcurrentHashMap(); + for (int i = 0; i < size; i++) { BI bi = new BI(i); BS bs = new BS(String.valueOf(i)); LexicographicList bis = new LexicographicList(bi); @@ -111,8 +144,8 @@ public class ConcurrentHashMapTest exten assertTrue(m.containsKey(bss)); assertTrue(m.containsKey(bis)); } - for (int i = 0; i < 1000; i++) { - assertTrue(m.containsKey(new ArrayList(Collections.singleton(new BI(i))))); + for (int i = 0; i < size; i++) { + assertTrue(m.containsKey(Collections.singletonList(new BI(i)))); } } @@ -122,18 +155,55 @@ public class ConcurrentHashMapTest exten * inserted and found. */ public void testGenericComparable2() { - ConcurrentHashMap m = new ConcurrentHashMap<>(); - for (int i = 0; i < 1000; i++) { - m.put(new ArrayList(Collections.singleton(new BI(i))), true); + int size = 500; // makes measured test run time -> 60ms + ConcurrentHashMap m = + new ConcurrentHashMap(); + for (int i = 0; i < size; i++) { + m.put(Collections.singletonList(new BI(i)), true); } - for (int i = 0; i < 1000; i++) { + for (int i = 0; i < size; i++) { LexicographicList bis = new LexicographicList(new BI(i)); assertTrue(m.containsKey(bis)); } } /** + * Mixtures of instances of comparable and non-comparable classes + * can be inserted and found. + */ + public void testMixedComparable() { + int size = 1200; // makes measured test run time -> 35ms + ConcurrentHashMap map = + new ConcurrentHashMap(); + Random rng = new Random(); + for (int i = 0; i < size; i++) { + Object x; + switch (rng.nextInt(4)) { + case 0: + x = new Object(); + break; + case 1: + x = new CollidingObject(Integer.toString(i)); + break; + default: + x = new ComparableCollidingObject(Integer.toString(i)); + } + assertNull(map.put(x, x)); + } + int count = 0; + for (Object k : map.keySet()) { + assertEquals(map.get(k), k); + ++count; + } + assertEquals(count, size); + assertEquals(map.size(), size); + for (Object k : map.keySet()) { + assertEquals(map.put(k, k), k); + } + } + + /** * clear removes all pairs */ public void testClear() { @@ -156,6 +226,17 @@ public class ConcurrentHashMapTest exten } /** + * hashCode() equals sum of each key.hashCode ^ value.hashCode + */ + public void testHashCode() { + ConcurrentHashMap map = map5(); + int sum = 0; + for (Map.Entry e : map.entrySet()) + sum += e.getKey().hashCode() ^ e.getValue().hashCode(); + assertEquals(sum, map.hashCode()); + } + + /** * contains returns true for contained value */ public void testContains() { @@ -206,6 +287,7 @@ public class ConcurrentHashMapTest exten assertEquals("A", (String)map.get(one)); ConcurrentHashMap empty = new ConcurrentHashMap(); assertNull(map.get("anything")); + assertNull(empty.get("anything")); } /** @@ -439,33 +521,73 @@ public class ConcurrentHashMapTest exten // Exception tests /** - * Cannot create with negative capacity + * Cannot create with only negative capacity */ public void testConstructor1() { try { - new ConcurrentHashMap(-1,0,1); + new ConcurrentHashMap(-1); shouldThrow(); } catch (IllegalArgumentException success) {} } /** - * Cannot create with negative concurrency level - */ + * Constructor (initialCapacity, loadFactor) throws + * IllegalArgumentException if either argument is negative + */ public void testConstructor2() { try { - new ConcurrentHashMap(1,0,-1); + new ConcurrentHashMap(-1, .75f); shouldThrow(); - } catch (IllegalArgumentException success) {} + } catch (IllegalArgumentException e) {} + + try { + new ConcurrentHashMap(16, -1); + shouldThrow(); + } catch (IllegalArgumentException e) {} } /** - * Cannot create with only negative capacity + * Constructor (initialCapacity, loadFactor, concurrencyLevel) + * throws IllegalArgumentException if any argument is negative */ public void testConstructor3() { try { - new ConcurrentHashMap(-1); + new ConcurrentHashMap(-1, .75f, 1); shouldThrow(); - } catch (IllegalArgumentException success) {} + } catch (IllegalArgumentException e) {} + + try { + new ConcurrentHashMap(16, -1, 1); + shouldThrow(); + } catch (IllegalArgumentException e) {} + + try { + new ConcurrentHashMap(16, .75f, -1); + shouldThrow(); + } catch (IllegalArgumentException e) {} + } + + /** + * ConcurrentHashMap(map) throws NullPointerException if the given + * map is null + */ + public void testConstructor4() { + try { + new ConcurrentHashMap(null); + shouldThrow(); + } catch (NullPointerException e) {} + } + + /** + * ConcurrentHashMap(map) creates a new map with the same mappings + * as the given map + */ + public void testConstructor5() { + ConcurrentHashMap map1 = map5(); + ConcurrentHashMap map2 = new ConcurrentHashMap(map5()); + assertTrue(map2.equals(map1)); + map2.put(one, "F"); + assertFalse(map2.equals(map1)); } /**