undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com Patches, unit tests, benchmarks, whitepaper, and outreach briefs. Public domain — no copyright claimed. Use freely.
This commit is contained in:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
70
test/jdk/java/util/HashMap/HashMapCloneLeak.java
Normal file
70
test/jdk/java/util/HashMap/HashMapCloneLeak.java
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 7042126
|
||||
* @summary Verify that we do not leak contents when we clone a HashMap
|
||||
* @author david.buck@oracle.com
|
||||
* @run main/othervm HashMapCloneLeak
|
||||
* @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+EliminateAutoBox -XX:AutoBoxCacheMax=20000 HashMapCloneLeak
|
||||
*/
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class HashMapCloneLeak {
|
||||
|
||||
static WeakReference<Object> wr = null;
|
||||
|
||||
// helper method to keep testObject and map out of main method's scope
|
||||
private static HashMap<Integer, Object> makeMap() {
|
||||
HashMap<Integer, Object> map = new HashMap<Integer, Object>();
|
||||
Object testObject = new Object();
|
||||
wr = new WeakReference<Object>(testObject);
|
||||
map.put(42, testObject);
|
||||
return map;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
HashMap<Integer, Object> hm = makeMap();
|
||||
hm = (HashMap<Integer, Object>)hm.clone();
|
||||
hm.clear();
|
||||
// There should no longer be a strong reference to testObject
|
||||
// the WeakReference should be nulled out by GC. If not,
|
||||
// we will hang here until timed out by the test harness.
|
||||
Object[] chain = null;
|
||||
while (wr.get() != null) {
|
||||
try {
|
||||
Object[] allocate = new Object[1000000];
|
||||
allocate[0] = chain;
|
||||
chain = allocate;
|
||||
} catch (OutOfMemoryError oome) {
|
||||
chain = null;
|
||||
}
|
||||
System.gc();
|
||||
Thread.sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
44
test/jdk/java/util/HashMap/KeySetRemove.java
Normal file
44
test/jdk/java/util/HashMap/KeySetRemove.java
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (c) 1999, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 4286765
|
||||
* @summary HashMap and TreeMap entrySet().remove(k) spuriously returned
|
||||
* false if the Map previously mapped k to null.
|
||||
*/
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public class KeySetRemove {
|
||||
public static void main(String[] args) throws Exception {
|
||||
Map[] m = {new HashMap(), new TreeMap()};
|
||||
for (int i=0; i<m.length; i++) {
|
||||
m[i].put("bananas", null);
|
||||
if (!m[i].keySet().remove("bananas"))
|
||||
throw new Exception("Yes, we have no bananas: "+i);
|
||||
}
|
||||
}
|
||||
}
|
||||
60
test/jdk/java/util/HashMap/NullKeyAtResize.java
Normal file
60
test/jdk/java/util/HashMap/NullKeyAtResize.java
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 7173432
|
||||
* @summary If the key to be inserted into a HashMap is null and the table
|
||||
* needs to be resized as part of the insertion then addEntry will try to
|
||||
* recalculate the hash of a null key. This will fail with an NPE.
|
||||
*/
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class NullKeyAtResize {
|
||||
public static void main(String[] args) throws Exception {
|
||||
List<Object> old_order = new ArrayList<>();
|
||||
Map<Object,Object> m = new HashMap<>(16);
|
||||
int number = 0;
|
||||
while(number < 100000) {
|
||||
m.put(null,null); // try to put in null. This may cause resize.
|
||||
m.remove(null); // remove it.
|
||||
Integer adding = (number += 100);
|
||||
m.put(adding, null); // try to put in a number. This wont cause resize.
|
||||
List<Object> new_order = new ArrayList<>();
|
||||
new_order.addAll(m.keySet());
|
||||
new_order.remove(adding);
|
||||
if(!old_order.equals(new_order)) {
|
||||
// we resized and didn't crash.
|
||||
System.out.println("Encountered resize after " + (number / 100) + " iterations");
|
||||
break;
|
||||
}
|
||||
// remember this order for the next time around.
|
||||
old_order.clear();
|
||||
old_order.addAll(m.keySet());
|
||||
}
|
||||
if(number == 100000) {
|
||||
throw new Error("Resize never occurred");
|
||||
}
|
||||
}
|
||||
}
|
||||
78
test/jdk/java/util/HashMap/OverrideIsEmpty.java
Normal file
78
test/jdk/java/util/HashMap/OverrideIsEmpty.java
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Portions Copyright (c) 2013 IBM Corporation
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8019381
|
||||
* @summary Verify that we do not get exception when we override isEmpty()
|
||||
* in a subclass of HashMap
|
||||
* @author zhangshj@linux.vnet.ibm.com
|
||||
*/
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class OverrideIsEmpty {
|
||||
public static class NotEmptyHashMap<K,V> extends HashMap<K,V> {
|
||||
private K alwaysExistingKey;
|
||||
private V alwaysExistingValue;
|
||||
|
||||
@Override
|
||||
public V get(Object key) {
|
||||
if (key == alwaysExistingKey) {
|
||||
return alwaysExistingValue;
|
||||
}
|
||||
return super.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return super.size() + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return size() == 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
NotEmptyHashMap<Object, Object> map = new NotEmptyHashMap<>();
|
||||
Object key = new Object();
|
||||
Object value = new Object();
|
||||
map.get(key);
|
||||
map.remove(key);
|
||||
map.replace(key, value, null);
|
||||
map.replace(key, value);
|
||||
map.computeIfPresent(key, new BiFunction<Object, Object, Object>() {
|
||||
public Object apply(Object key, Object oldValue) {
|
||||
return oldValue;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
90
test/jdk/java/util/HashMap/PutNullKey.java
Normal file
90
test/jdk/java/util/HashMap/PutNullKey.java
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8046085
|
||||
* @summary Ensure that when trees are being used for collisions that null key
|
||||
* insertion still works.
|
||||
*/
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class PutNullKey {
|
||||
|
||||
// Initial capacity of map
|
||||
// Should be >= the map capacity for treeifying, see HashMap/ConcurrentMap.MIN_TREEIFY_CAPACITY
|
||||
static final int INITIAL_CAPACITY = 64;
|
||||
|
||||
// Maximum size of map
|
||||
// Should be > the treeify threshold, see HashMap/ConcurrentMap.TREEIFY_THRESHOLD
|
||||
static final int SIZE = 256;
|
||||
|
||||
// Load factor of map
|
||||
// A value 1.0 will ensure that a new threshold == capacity
|
||||
static final float LOAD_FACTOR = 1.0f;
|
||||
|
||||
public static class CollidingHash implements Comparable<CollidingHash> {
|
||||
|
||||
private final int value;
|
||||
|
||||
public CollidingHash(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
// intentionally bad hashcode. Force into first bin.
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (null == o) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (o.getClass() != CollidingHash.class) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return value == ((CollidingHash) o).value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(CollidingHash o) {
|
||||
return value - o.value;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Map<Object,Object> m = new HashMap<>(INITIAL_CAPACITY, LOAD_FACTOR);
|
||||
IntStream.range(0, SIZE)
|
||||
.mapToObj(CollidingHash::new)
|
||||
.forEach(e -> { m.put(e, e); });
|
||||
|
||||
// kaboom?
|
||||
m.put(null, null);
|
||||
}
|
||||
}
|
||||
101
test/jdk/java/util/HashMap/ReplaceExisting.java
Normal file
101
test/jdk/java/util/HashMap/ReplaceExisting.java
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8025173
|
||||
* @summary Verify that replacing the value for an existing key does not
|
||||
* corrupt active iterators, in particular due to a resize() occurring and
|
||||
* not updating modCount.
|
||||
* @run main ReplaceExisting
|
||||
*/
|
||||
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class ReplaceExisting {
|
||||
/* Number of entries required to trigger a resize for cap=16, load=0.75*/
|
||||
private static int ENTRIES = 13;
|
||||
|
||||
public static void main(String[] args) {
|
||||
for (int i = 0; i <= ENTRIES; i++) {
|
||||
HashMap<Integer,Integer> hm = prepHashMap();
|
||||
testItr(hm, i);
|
||||
}
|
||||
}
|
||||
|
||||
/* Prepare a HashMap that will resize on next put() */
|
||||
private static HashMap<Integer,Integer> prepHashMap() {
|
||||
HashMap<Integer,Integer> hm = new HashMap<>(16, 0.75f);
|
||||
// Add items to one more than the resize threshold
|
||||
for (int i = 0; i < ENTRIES; i++) {
|
||||
hm.put(i*10, i*10);
|
||||
}
|
||||
return hm;
|
||||
}
|
||||
|
||||
/* Iterate hm for elemBeforePut elements, then call put() to replace value
|
||||
* for existing key. With bug 8025173, this will also cause a resize, but
|
||||
* not increase the modCount.
|
||||
* Finish the iteration to check for a corrupt iterator.
|
||||
*/
|
||||
private static void testItr(HashMap<Integer,Integer> hm, int elemBeforePut) {
|
||||
if (elemBeforePut > hm.size()) {
|
||||
throw new IllegalArgumentException("Error in test: elemBeforePut must be <= HashMap size");
|
||||
}
|
||||
// Create a copy of the keys
|
||||
HashSet<Integer> keys = new HashSet<>(hm.size());
|
||||
keys.addAll(hm.keySet());
|
||||
|
||||
HashSet<Integer> collected = new HashSet<>(hm.size());
|
||||
|
||||
// Run itr for elemBeforePut items, collecting returned elems
|
||||
Iterator<Integer> itr = hm.keySet().iterator();
|
||||
for (int i = 0; i < elemBeforePut; i++) {
|
||||
Integer retVal = itr.next();
|
||||
if (!collected.add(retVal)) {
|
||||
throw new RuntimeException("Corrupt iterator: key " + retVal + " already encountered");
|
||||
}
|
||||
}
|
||||
|
||||
// Do put() to replace entry (and resize table when bug present)
|
||||
if (null == hm.put(0, 100)) {
|
||||
throw new RuntimeException("Error in test: expected key 0 to be in the HashMap");
|
||||
}
|
||||
|
||||
// Finish itr + collecting returned elems
|
||||
while(itr.hasNext()) {
|
||||
Integer retVal = itr.next();
|
||||
if (!collected.add(retVal)) {
|
||||
throw new RuntimeException("Corrupt iterator: key " + retVal + " already encountered");
|
||||
}
|
||||
}
|
||||
|
||||
// Compare returned elems to original copy of keys
|
||||
if (!keys.equals(collected)) {
|
||||
throw new RuntimeException("Collected keys do not match original set of keys");
|
||||
}
|
||||
}
|
||||
}
|
||||
47
test/jdk/java/util/HashMap/SetValue.java
Normal file
47
test/jdk/java/util/HashMap/SetValue.java
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 4627516
|
||||
* @summary HashMap.Entry.setValue() returns new value (as opposed to old)
|
||||
* @author jbloch
|
||||
*/
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class SetValue {
|
||||
static final String key = "key";
|
||||
static final String oldValue = "old";
|
||||
static final String newValue = "new";
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Map m = new HashMap();
|
||||
m.put(key, oldValue);
|
||||
Map.Entry e = (Map.Entry) m.entrySet().iterator().next();
|
||||
Object returnVal = e.setValue(newValue);
|
||||
if (!returnVal.equals(oldValue))
|
||||
throw new RuntimeException("Return value: " + returnVal);
|
||||
}
|
||||
}
|
||||
155
test/jdk/java/util/HashMap/ToArray.java
Normal file
155
test/jdk/java/util/HashMap/ToArray.java
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.LongStream;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary HashMap.toArray() behavior tests
|
||||
* @author tvaleev
|
||||
*/
|
||||
public class ToArray {
|
||||
public static void main(String[] args) {
|
||||
checkMap(false);
|
||||
checkMap(true);
|
||||
checkSet(false);
|
||||
checkSet(true);
|
||||
}
|
||||
|
||||
private static <T extends Comparable<T>> void checkToArray(String message, T[] expected, Collection<T> collection,
|
||||
boolean ignoreOrder) {
|
||||
if (ignoreOrder) {
|
||||
Arrays.sort(expected);
|
||||
}
|
||||
checkToObjectArray(message, expected, collection, ignoreOrder);
|
||||
checkToTypedArray(message, expected, Arrays.copyOf(expected, 0), collection, ignoreOrder);
|
||||
checkToTypedArray(message, expected, expected.clone(), collection, ignoreOrder);
|
||||
if (expected.length > 0) {
|
||||
T[] biggerArray = Arrays.copyOf(expected, expected.length * 2);
|
||||
System.arraycopy(expected, 0, biggerArray, expected.length, expected.length);
|
||||
checkToTypedArray(message, expected, biggerArray, collection, ignoreOrder);
|
||||
}
|
||||
}
|
||||
|
||||
private static <T extends Comparable<T>> void checkToTypedArray(String message, T[] expected, T[] inputArray,
|
||||
Collection<T> collection, boolean ignoreOrder) {
|
||||
T[] res = collection.toArray(inputArray);
|
||||
if (expected.length <= inputArray.length && res != inputArray) {
|
||||
throw new AssertionError(message + ": not the same array returned");
|
||||
}
|
||||
if (res.getClass() != expected.getClass()) {
|
||||
throw new AssertionError(message + ": wrong class returned: " + res.getClass());
|
||||
}
|
||||
if (res.length < expected.length) {
|
||||
throw new AssertionError(message + ": length is smaller than expected: " + res.length + " < " + expected.length);
|
||||
}
|
||||
if (ignoreOrder) {
|
||||
Arrays.sort(res, 0, Math.min(res.length, expected.length));
|
||||
}
|
||||
if (inputArray.length <= expected.length) {
|
||||
if (!Arrays.equals(res, expected)) {
|
||||
throw new AssertionError(message + ": not equal: " + Arrays.toString(expected) + " != " +
|
||||
Arrays.toString(res));
|
||||
}
|
||||
} else {
|
||||
int mismatch = Arrays.mismatch(expected, res);
|
||||
if (mismatch != expected.length) {
|
||||
throw new AssertionError(message + ": mismatch at " + mismatch);
|
||||
}
|
||||
if (res[expected.length] != null) {
|
||||
throw new AssertionError(message + ": no null at position " + expected.length);
|
||||
}
|
||||
// The tail of bigger array after expected.length position must be untouched
|
||||
mismatch = Arrays
|
||||
.mismatch(expected, 1, expected.length, res, expected.length + 1, res.length);
|
||||
if (mismatch != -1) {
|
||||
throw new AssertionError(message + ": mismatch at " + mismatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static <T extends Comparable<T>> void checkToObjectArray(String message, T[] expected,
|
||||
Collection<T> collection, boolean ignoreOrder) {
|
||||
Object[] objects = collection.toArray();
|
||||
if (objects.getClass() != Object[].class) {
|
||||
throw new AssertionError(message + ": wrong class returned: " + objects.getClass());
|
||||
}
|
||||
if (ignoreOrder) {
|
||||
Arrays.sort(objects);
|
||||
}
|
||||
int mismatch = Arrays.mismatch(expected, objects);
|
||||
if (mismatch != -1) {
|
||||
throw new AssertionError(message + ": mismatch at " + mismatch);
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkMap(boolean ordered) {
|
||||
Map<String, String> map = ordered ? new LinkedHashMap<>() : new HashMap<>();
|
||||
checkToArray("Empty-keys", new String[0], map.keySet(), !ordered);
|
||||
checkToArray("Empty-values", new String[0], map.values(), !ordered);
|
||||
|
||||
List<String> keys = new ArrayList<>();
|
||||
List<String> values = new ArrayList<>();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
keys.add(String.valueOf(i));
|
||||
values.add(String.valueOf(i * 2));
|
||||
map.put(String.valueOf(i), String.valueOf(i * 2));
|
||||
checkToArray(i + "-keys", keys.toArray(new String[0]), map.keySet(), !ordered);
|
||||
checkToArray(i + "-values", values.toArray(new String[0]), map.values(), !ordered);
|
||||
}
|
||||
map.clear();
|
||||
checkToArray("Empty-keys", new String[0], map.keySet(), !ordered);
|
||||
checkToArray("Empty-values", new String[0], map.values(), !ordered);
|
||||
}
|
||||
|
||||
private static void checkSet(boolean ordered) {
|
||||
Collection<String> set = ordered ? new LinkedHashSet<>() : new HashSet<>();
|
||||
checkToArray("Empty", new String[0], set, !ordered);
|
||||
set.add("foo");
|
||||
checkToArray("One", new String[]{"foo"}, set, !ordered);
|
||||
set.add("bar");
|
||||
checkToArray("Two", new String[]{"foo", "bar"}, set, !ordered);
|
||||
|
||||
Collection<Long> longSet = ordered ? new LinkedHashSet<>() : new HashSet<>();
|
||||
for (int x = 0; x < 100; x++) {
|
||||
longSet.add((long) x);
|
||||
}
|
||||
checkToArray("100", LongStream.range(0, 100).boxed().toArray(Long[]::new), longSet, !ordered);
|
||||
longSet.clear();
|
||||
checkToArray("After clear", new Long[0], longSet, !ordered);
|
||||
for (int x = 0; x < 100; x++) {
|
||||
longSet.add(((long) x) | (((long) x) << 32));
|
||||
}
|
||||
checkToArray("Collisions", LongStream.range(0, 100).mapToObj(x -> x | (x << 32))
|
||||
.toArray(Long[]::new), longSet, !ordered);
|
||||
}
|
||||
}
|
||||
40
test/jdk/java/util/HashMap/ToString.java
Normal file
40
test/jdk/java/util/HashMap/ToString.java
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (c) 1999, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 4189821
|
||||
* @summary HashMap's entry.toString threw a null pointer exc if the HashMap
|
||||
* contained null keys or values.
|
||||
*/
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ToString {
|
||||
public static void main(String[] args) throws Exception {
|
||||
Map m = new HashMap();
|
||||
m.put(null, null);
|
||||
m.entrySet().iterator().next().toString();
|
||||
}
|
||||
}
|
||||
175
test/jdk/java/util/HashMap/TreeBinAssert.java
Normal file
175
test/jdk/java/util/HashMap/TreeBinAssert.java
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8205399
|
||||
* @summary Check for AssertionError from HashMap TreeBin after Iterator.remove
|
||||
* @run testng/othervm -esa TreeBinAssert
|
||||
*/
|
||||
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
import org.testng.annotations.BeforeTest;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Iterator;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class TreeBinAssert {
|
||||
private static final int ITR_RM = -1; // Remove an item via Iterator
|
||||
private static final int BIN352442_SIZE = 524288;
|
||||
private static final int BIN552165_SIZE = 1048576;
|
||||
|
||||
@DataProvider(name = "SizeAndHashes")
|
||||
public Object[][] sizeAndHashes() {
|
||||
return new Object[][] {
|
||||
{ // Bin 352442
|
||||
BIN352442_SIZE,
|
||||
new int[] {
|
||||
2020958394,
|
||||
631595194,
|
||||
1984782522,
|
||||
419782842,
|
||||
285565114,
|
||||
1432182970,
|
||||
841310394,
|
||||
320692410,
|
||||
303390906,
|
||||
ITR_RM,
|
||||
ITR_RM,
|
||||
ITR_RM,
|
||||
ITR_RM,
|
||||
ITR_RM,
|
||||
ITR_RM,
|
||||
ITR_RM,
|
||||
ITR_RM,
|
||||
519397562,
|
||||
ITR_RM,
|
||||
626352314,
|
||||
101540026
|
||||
}
|
||||
},{ // Bin 552165
|
||||
BIN552165_SIZE,
|
||||
new int[] {
|
||||
790129893,
|
||||
1214803173,
|
||||
1212706021,
|
||||
608726245,
|
||||
2073586917,
|
||||
1433955557,
|
||||
692612325,
|
||||
370699493,
|
||||
2061004005,
|
||||
48786661,
|
||||
ITR_RM,
|
||||
ITR_RM,
|
||||
1418226917,
|
||||
ITR_RM,
|
||||
ITR_RM,
|
||||
ITR_RM,
|
||||
ITR_RM,
|
||||
ITR_RM,
|
||||
ITR_RM,
|
||||
ITR_RM,
|
||||
1487432933,
|
||||
ITR_RM,
|
||||
ITR_RM,
|
||||
1880648933,
|
||||
338193637
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@BeforeTest
|
||||
public void checkAssertionStatus() {
|
||||
if (!HashMap.class.desiredAssertionStatus()) {
|
||||
System.out.println("*** Superficial test run. Test should be run with -esa ***\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider = "SizeAndHashes")
|
||||
public void testMap(int size, int[] hashes) {
|
||||
Map<Key,Integer> map = new HashMap<>(size);
|
||||
|
||||
doTest(map, hashes,
|
||||
(c,k) -> { ((Map<Key,Integer>)c).put(k,0); },
|
||||
(c) -> { return ((Map<Key,Integer>)c).keySet().iterator(); }
|
||||
);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "SizeAndHashes")
|
||||
public void testSet(int size, int[] hashes) {
|
||||
Set<Key> set = new LinkedHashSet<>(size);
|
||||
|
||||
doTest(set, hashes,
|
||||
(c,k) -> { ((Set<Key>)c).add(k); },
|
||||
(c) -> { return ((Set<Key>)c).iterator(); }
|
||||
);
|
||||
}
|
||||
|
||||
private void doTest(Object collection, int[] hashes,
|
||||
BiConsumer<Object,Key> addKey,
|
||||
Function<Object,Iterator<Key>> mkItr) {
|
||||
Iterator<Key> itr = null; // saved iterator, used for removals
|
||||
for (int h : hashes) {
|
||||
if (h == ITR_RM) {
|
||||
if (itr == null) {
|
||||
itr = mkItr.apply(collection);
|
||||
}
|
||||
itr.next();
|
||||
itr.remove();
|
||||
} else {
|
||||
itr = null;
|
||||
addKey.accept(collection, new Key(h));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that will have specified hash code in a HashMap.
|
||||
*/
|
||||
static class Key implements Comparable<Key> {
|
||||
final int hash;
|
||||
|
||||
public Key(int desiredHash) {
|
||||
// Account for processing done by HashMap
|
||||
this.hash = desiredHash ^ (desiredHash >>> 16);
|
||||
}
|
||||
|
||||
@Override public int hashCode() { return this.hash; }
|
||||
|
||||
@Override public boolean equals(Object o) {
|
||||
return o.hashCode() == this.hashCode();
|
||||
}
|
||||
|
||||
@Override public int compareTo(Key k) {
|
||||
return Integer.compare(this.hash, k.hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
455
test/jdk/java/util/HashMap/WhiteBoxResizeTest.java
Normal file
455
test/jdk/java/util/HashMap/WhiteBoxResizeTest.java
Normal file
|
|
@ -0,0 +1,455 @@
|
|||
/*
|
||||
* Copyright (c) 2018, Red Hat, Inc. All rights reserved.
|
||||
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.IntFunction;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertNull;
|
||||
import static org.testng.Assert.assertThrows;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8186958 8210280 8281631 8285386 8284780
|
||||
* @modules java.base/java.util:open
|
||||
* @summary White box tests for HashMap-related internals around table sizing
|
||||
* @comment skip running this test on 32 bit VM
|
||||
* @requires vm.bits == "64"
|
||||
* @run testng/othervm/timeout=960 -Xmx2g WhiteBoxResizeTest
|
||||
*/
|
||||
public class WhiteBoxResizeTest {
|
||||
|
||||
final MethodHandle TABLE_SIZE_FOR;
|
||||
final VarHandle HM_TABLE;
|
||||
final VarHandle WHM_TABLE;
|
||||
final VarHandle HS_MAP;
|
||||
|
||||
public WhiteBoxResizeTest() throws ReflectiveOperationException {
|
||||
MethodHandles.Lookup hmlookup = MethodHandles.privateLookupIn(HashMap.class, MethodHandles.lookup());
|
||||
TABLE_SIZE_FOR = hmlookup.findStatic(
|
||||
HashMap.class, "tableSizeFor", MethodType.methodType(int.class, int.class));
|
||||
HM_TABLE = hmlookup.unreflectVarHandle(HashMap.class.getDeclaredField("table"));
|
||||
|
||||
MethodHandles.Lookup whmlookup = MethodHandles.privateLookupIn(WeakHashMap.class, MethodHandles.lookup());
|
||||
WHM_TABLE = whmlookup.unreflectVarHandle(WeakHashMap.class.getDeclaredField("table"));
|
||||
|
||||
MethodHandles.Lookup hslookup = MethodHandles.privateLookupIn(HashSet.class, MethodHandles.lookup());
|
||||
HS_MAP = hslookup.unreflectVarHandle(HashSet.class.getDeclaredField("map"));
|
||||
}
|
||||
|
||||
/*
|
||||
* utility methods
|
||||
*/
|
||||
|
||||
int tableSizeFor(int n) {
|
||||
try {
|
||||
return (int) TABLE_SIZE_FOR.invoke(n);
|
||||
} catch (Throwable t) {
|
||||
throw new AssertionError(t);
|
||||
}
|
||||
}
|
||||
|
||||
Object[] table(Map<?, ?> map) {
|
||||
try {
|
||||
VarHandle vh = map instanceof WeakHashMap ? WHM_TABLE : HM_TABLE;
|
||||
return (Object[]) vh.get(map);
|
||||
} catch (Throwable t) {
|
||||
throw new AssertionError(t);
|
||||
}
|
||||
}
|
||||
|
||||
int capacity(Map<?, ?> map) {
|
||||
return table(map).length;
|
||||
}
|
||||
|
||||
// creates a map with size mappings
|
||||
Map<String, String> makeMap(int size) {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
putN(map, size);
|
||||
return map;
|
||||
}
|
||||
|
||||
// creates a "fake" map: size() returns the given size, but
|
||||
// the entrySet iterator returns only one entry
|
||||
Map<String, String> fakeMap(int size) {
|
||||
return new AbstractMap<>() {
|
||||
public Set<Map.Entry<String, String>> entrySet() {
|
||||
return new AbstractSet<Map.Entry<String, String>>() {
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public Iterator<Map.Entry<String, String>> iterator() {
|
||||
return Set.of(Map.entry("1", "1")).iterator();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void putN(Map<String, String> map, int n) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
String string = Integer.toString(i);
|
||||
map.put(string, string);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* tests of tableSizeFor
|
||||
*/
|
||||
|
||||
@DataProvider(name = "tableSizeFor")
|
||||
public Object[][] tableSizeForCases() {
|
||||
final int MAX = 1 << 30;
|
||||
return new Object[][] {
|
||||
// tableSizeFor(arg), expected
|
||||
{ 0, 1 },
|
||||
{ 1, 1 },
|
||||
{ 2, 2 },
|
||||
{ 3, 4 },
|
||||
{ 4, 4 },
|
||||
{ 5, 8 },
|
||||
{ 15, 16 },
|
||||
{ 16, 16 },
|
||||
{ 17, 32 },
|
||||
{ MAX-1, MAX },
|
||||
{ MAX, MAX },
|
||||
{ MAX+1, MAX },
|
||||
{ Integer.MAX_VALUE, MAX }
|
||||
};
|
||||
}
|
||||
|
||||
@Test(dataProvider = "tableSizeFor")
|
||||
public void tableSizeFor(int arg, int expected) {
|
||||
assertEquals(tableSizeFor(arg), expected);
|
||||
}
|
||||
|
||||
/*
|
||||
* tests for lazy table allocation
|
||||
*/
|
||||
|
||||
@DataProvider(name = "lazy")
|
||||
public Object[][] lazyTableAllocationCases() {
|
||||
return new Object[][]{
|
||||
{new HashMap<>()},
|
||||
// { new WeakHashMap<>() }, // WHM doesn't allocate lazily
|
||||
{new LinkedHashMap<>()}
|
||||
};
|
||||
}
|
||||
|
||||
@Test(dataProvider = "lazy")
|
||||
public void lazyTableAllocation(Map<?, ?> map) {
|
||||
assertNull(table(map));
|
||||
}
|
||||
|
||||
/*
|
||||
* tests for default capacity (no-arg constructor)
|
||||
*/
|
||||
|
||||
@DataProvider(name = "defaultCapacity")
|
||||
public Object[][] defaultCapacityCases() {
|
||||
return new Supplier<?>[][]{
|
||||
{() -> new HashMap<>()},
|
||||
{() -> new LinkedHashMap<>()},
|
||||
{() -> new WeakHashMap<>()}
|
||||
};
|
||||
}
|
||||
|
||||
@Test(dataProvider = "defaultCapacity")
|
||||
public void defaultCapacity(Supplier<Map<String, String>> s) {
|
||||
Map<String, String> map = s.get();
|
||||
map.put("", "");
|
||||
assertEquals(capacity(map), 16);
|
||||
}
|
||||
|
||||
/*
|
||||
* tests for requested capacity (int and int+float constructors)
|
||||
*/
|
||||
|
||||
@DataProvider(name = "requestedCapacity")
|
||||
public Iterator<Object[]> requestedCapacityCases() {
|
||||
ArrayList<Object[]> cases = new ArrayList<>();
|
||||
for (int i = 2; i < 64; i++) {
|
||||
int cap = i;
|
||||
cases.add(new Object[]{"rhm1", cap, (Supplier<Map<String, String>>) () -> new HashMap<>(cap)});
|
||||
cases.add(new Object[]{"rhm2", cap, (Supplier<Map<String, String>>) () -> new HashMap<>(cap, 0.75f)});
|
||||
cases.add(new Object[]{"rlm1", cap, (Supplier<Map<String, String>>) () -> new LinkedHashMap<>(cap)});
|
||||
cases.add(new Object[]{"rlm2", cap, (Supplier<Map<String, String>>) () -> new LinkedHashMap<>(cap, 0.75f)});
|
||||
cases.add(new Object[]{"rwm1", cap, (Supplier<Map<String, String>>) () -> new WeakHashMap<>(cap)});
|
||||
cases.add(new Object[]{"rwm2", cap, (Supplier<Map<String, String>>) () -> new WeakHashMap<>(cap, 0.75f)});
|
||||
}
|
||||
return cases.iterator();
|
||||
}
|
||||
|
||||
@Test(dataProvider = "requestedCapacity")
|
||||
public void requestedCapacity(String label, int cap, Supplier<Map<String, String>> s) {
|
||||
Map<String, String> map = s.get();
|
||||
map.put("", "");
|
||||
assertEquals(capacity(map), tableSizeFor(cap));
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests for capacity after map is populated with a given number N of mappings.
|
||||
* Maps are populated using a copy constructor on a map with N mappings,
|
||||
* other constructors followed by N put() calls, and other constructors followed
|
||||
* by putAll() on a map with N mappings.
|
||||
*
|
||||
* String labels encode the test case for ease of diagnosis if one of the test cases fails.
|
||||
* For example, "plm2pn" is "populated LinkedHashMap, 2-arg constructor, followed by putN".
|
||||
*/
|
||||
|
||||
// helper method for one populated capacity case, to provide target types for lambdas
|
||||
Object[] pcc(String label,
|
||||
int size,
|
||||
int expectedCapacity,
|
||||
Supplier<Map<String, String>> supplier,
|
||||
Consumer<Map<String, String>> consumer) {
|
||||
return new Object[]{label, size, expectedCapacity, supplier, consumer};
|
||||
}
|
||||
|
||||
List<Object[]> genPopulatedCapacityCases(int size, int cap) {
|
||||
return Arrays.asList(
|
||||
pcc("phmcpy", size, cap, () -> new HashMap<>(makeMap(size)), map -> { }),
|
||||
pcc("phm0pn", size, cap, () -> new HashMap<>(), map -> { putN(map, size); }),
|
||||
pcc("phm1pn", size, cap, () -> new HashMap<>(cap), map -> { putN(map, size); }),
|
||||
pcc("phm2pn", size, cap, () -> new HashMap<>(cap, 0.75f), map -> { putN(map, size); }),
|
||||
pcc("phm0pa", size, cap, () -> new HashMap<>(), map -> { map.putAll(makeMap(size)); }),
|
||||
pcc("phm1pa", size, cap, () -> new HashMap<>(cap), map -> { map.putAll(makeMap(size)); }),
|
||||
pcc("phm2pa", size, cap, () -> new HashMap<>(cap, 0.75f), map -> { map.putAll(makeMap(size)); }),
|
||||
|
||||
pcc("plmcpy", size, cap, () -> new LinkedHashMap<>(makeMap(size)), map -> { }),
|
||||
pcc("plm0pn", size, cap, () -> new LinkedHashMap<>(), map -> { putN(map, size); }),
|
||||
pcc("plm1pn", size, cap, () -> new LinkedHashMap<>(cap), map -> { putN(map, size); }),
|
||||
pcc("plm2pn", size, cap, () -> new LinkedHashMap<>(cap, 0.75f), map -> { putN(map, size); }),
|
||||
pcc("plm0pa", size, cap, () -> new LinkedHashMap<>(), map -> { map.putAll(makeMap(size)); }),
|
||||
pcc("plm1pa", size, cap, () -> new LinkedHashMap<>(cap), map -> { map.putAll(makeMap(size)); }),
|
||||
pcc("plm2pa", size, cap, () -> new LinkedHashMap<>(cap, 0.75f), map -> { map.putAll(makeMap(size)); }),
|
||||
|
||||
pcc("pwmcpy", size, cap, () -> new WeakHashMap<>(makeMap(size)), map -> { }),
|
||||
pcc("pwm0pn", size, cap, () -> new WeakHashMap<>(), map -> { putN(map, size); }),
|
||||
pcc("pwm1pn", size, cap, () -> new WeakHashMap<>(cap), map -> { putN(map, size); }),
|
||||
pcc("pwm2pn", size, cap, () -> new WeakHashMap<>(cap, 0.75f), map -> { putN(map, size); }),
|
||||
pcc("pwm0pa", size, cap, () -> new WeakHashMap<>(), map -> { map.putAll(makeMap(size)); }),
|
||||
pcc("pwm1pa", size, cap, () -> new WeakHashMap<>(cap), map -> { map.putAll(makeMap(size)); }),
|
||||
pcc("pwm2pa", size, cap, () -> new WeakHashMap<>(cap, 0.75f), map -> { map.putAll(makeMap(size)); })
|
||||
);
|
||||
}
|
||||
|
||||
List<Object[]> genFakePopulatedCapacityCases(int size, int cap) {
|
||||
return Arrays.asList(
|
||||
pcc("fhmcpy", size, cap, () -> new HashMap<>(fakeMap(size)), map -> { }),
|
||||
pcc("fhm0pa", size, cap, () -> new HashMap<>(), map -> { map.putAll(fakeMap(size)); }),
|
||||
pcc("fhm1pa", size, cap, () -> new HashMap<>(cap), map -> { map.putAll(fakeMap(size)); }),
|
||||
pcc("fhm2pa", size, cap, () -> new HashMap<>(cap, 0.75f), map -> { map.putAll(fakeMap(size)); }),
|
||||
|
||||
pcc("flmcpy", size, cap, () -> new LinkedHashMap<>(fakeMap(size)), map -> { }),
|
||||
pcc("flm0pa", size, cap, () -> new LinkedHashMap<>(), map -> { map.putAll(fakeMap(size)); }),
|
||||
pcc("flm1pa", size, cap, () -> new LinkedHashMap<>(cap), map -> { map.putAll(fakeMap(size)); }),
|
||||
pcc("flm2pa", size, cap, () -> new LinkedHashMap<>(cap, 0.75f), map -> { map.putAll(fakeMap(size)); }),
|
||||
|
||||
pcc("fwmcpy", size, cap, () -> new WeakHashMap<>(fakeMap(size)), map -> { }),
|
||||
// pcc("fwm0pa", size, cap, () -> new WeakHashMap<>(), map -> { map.putAll(fakeMap(size)); }), // see note
|
||||
pcc("fwm1pa", size, cap, () -> new WeakHashMap<>(cap), map -> { map.putAll(fakeMap(size)); }),
|
||||
pcc("fwm2pa", size, cap, () -> new WeakHashMap<>(cap, 0.75f), map -> { map.putAll(fakeMap(size)); })
|
||||
);
|
||||
|
||||
// Test case "fwm0pa" is commented out because WeakHashMap uses a different allocation
|
||||
// policy from the other map implementations: it deliberately under-allocates in this case.
|
||||
}
|
||||
|
||||
@DataProvider(name = "populatedCapacity")
|
||||
public Iterator<Object[]> populatedCapacityCases() {
|
||||
ArrayList<Object[]> cases = new ArrayList<>();
|
||||
cases.addAll(genPopulatedCapacityCases(12, 16));
|
||||
cases.addAll(genPopulatedCapacityCases(13, 32));
|
||||
cases.addAll(genPopulatedCapacityCases(24, 32));
|
||||
cases.addAll(genPopulatedCapacityCases(25, 64));
|
||||
cases.addAll(genPopulatedCapacityCases(48, 64));
|
||||
cases.addAll(genPopulatedCapacityCases(49, 128));
|
||||
|
||||
// numbers in this range are truncated by a float computation with 0.75f
|
||||
// but can get an exact result with a double computation with 0.75d
|
||||
cases.addAll(genFakePopulatedCapacityCases(25165824, 33554432));
|
||||
cases.addAll(genFakePopulatedCapacityCases(25165825, 67108864));
|
||||
cases.addAll(genFakePopulatedCapacityCases(50331648, 67108864));
|
||||
cases.addAll(genFakePopulatedCapacityCases(50331649, 134217728));
|
||||
|
||||
return cases.iterator();
|
||||
}
|
||||
|
||||
@Test(dataProvider = "populatedCapacity")
|
||||
public void populatedCapacity(String label, // unused, included for diagnostics
|
||||
int size, // unused, included for diagnostics
|
||||
int expectedCapacity,
|
||||
Supplier<Map<String, String>> s,
|
||||
Consumer<Map<String, String>> c) {
|
||||
Map<String, String> map = s.get();
|
||||
c.accept(map);
|
||||
assertEquals(capacity(map), expectedCapacity);
|
||||
}
|
||||
|
||||
/*
|
||||
* tests for requested size (static factory methods)
|
||||
*/
|
||||
|
||||
// helper method for one requested size case, to provide target types for lambda
|
||||
Object[] rsc(String label,
|
||||
int size,
|
||||
int expectedCapacity,
|
||||
Supplier<Capacitiable> supplier) {
|
||||
return new Object[]{label, size, expectedCapacity, supplier};
|
||||
}
|
||||
|
||||
List<Object[]> genRequestedSizeCases(int size, int cap) {
|
||||
return Arrays.asList(
|
||||
rsc("rshm", size, cap, () -> new MapCapacitiable(HashMap.newHashMap(size))),
|
||||
rsc("rslm", size, cap, () -> new MapCapacitiable(LinkedHashMap.newLinkedHashMap(size))),
|
||||
rsc("rswm", size, cap, () -> new MapCapacitiable(WeakHashMap.newWeakHashMap(size))),
|
||||
rsc("rshs", size, cap, () -> new SetCapacitiable(HashSet.newHashSet(size))),
|
||||
rsc("rsls", size, cap, () -> new SetCapacitiable(LinkedHashSet.newLinkedHashSet(size)))
|
||||
);
|
||||
}
|
||||
|
||||
@DataProvider(name = "requestedSize")
|
||||
public Iterator<Object[]> requestedSizeCases() {
|
||||
ArrayList<Object[]> cases = new ArrayList<>();
|
||||
cases.addAll(genRequestedSizeCases(12, 16));
|
||||
cases.addAll(genRequestedSizeCases(13, 32));
|
||||
cases.addAll(genRequestedSizeCases(24, 32));
|
||||
cases.addAll(genRequestedSizeCases(25, 64));
|
||||
cases.addAll(genRequestedSizeCases(48, 64));
|
||||
cases.addAll(genRequestedSizeCases(49, 128));
|
||||
|
||||
// numbers in this range are truncated by a float computation with 0.75f
|
||||
// but can get an exact result with a double computation with 0.75d
|
||||
cases.addAll(genRequestedSizeCases(25165824, 33554432));
|
||||
cases.addAll(genRequestedSizeCases(25165825, 67108864));
|
||||
cases.addAll(genRequestedSizeCases(50331648, 67108864));
|
||||
cases.addAll(genRequestedSizeCases(50331649, 134217728));
|
||||
|
||||
return cases.iterator();
|
||||
}
|
||||
|
||||
@Test(dataProvider = "requestedSize")
|
||||
public void requestedSize(String label, // unused, included for diagnostics
|
||||
int size, // unused, included for diagnostics
|
||||
int expectedCapacity,
|
||||
Supplier<Capacitiable> s) {
|
||||
Capacitiable capacitiable = s.get();
|
||||
capacitiable.init();
|
||||
assertEquals(capacitiable.capacity(), expectedCapacity);
|
||||
}
|
||||
|
||||
interface Capacitiable {
|
||||
|
||||
void init();
|
||||
|
||||
int capacity();
|
||||
|
||||
}
|
||||
|
||||
class MapCapacitiable implements Capacitiable {
|
||||
|
||||
private final Map<String, String> content;
|
||||
|
||||
public MapCapacitiable(Map<String, String> content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
content.put("", "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int capacity() {
|
||||
return table(content).length;
|
||||
}
|
||||
}
|
||||
|
||||
class SetCapacitiable implements Capacitiable {
|
||||
|
||||
private final Set<String> content;
|
||||
|
||||
public SetCapacitiable(Set<String> content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
content.add("");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int capacity() {
|
||||
HashMap<?, ?> hashMap = (HashMap<?, ?>) HS_MAP.get(content);
|
||||
return table(hashMap).length;
|
||||
}
|
||||
}
|
||||
|
||||
@DataProvider(name = "negativeNumMappings")
|
||||
public Iterator<Object[]> negativeNumMappings() {
|
||||
final List<Object[]> methods = new ArrayList<>();
|
||||
methods.add(new Object[] {(IntFunction<?>) HashMap::newHashMap, "HashMap::newHashMap"});
|
||||
methods.add(new Object[] {(IntFunction<?>) LinkedHashMap::newLinkedHashMap,
|
||||
"LinkedHashMap::newLinkedHashMap"});
|
||||
methods.add(new Object[] {(IntFunction<?>) WeakHashMap::newWeakHashMap,
|
||||
"WeakHashMap::newWeakHashMap"});
|
||||
methods.add(new Object[] {(IntFunction<?>) HashSet::newHashSet, "HashSet::newHashSet"});
|
||||
methods.add(new Object[] {(IntFunction<?>) LinkedHashSet::newLinkedHashSet,
|
||||
"LinkedHashSet::newLinkedHashSet"});
|
||||
return methods.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the APIs that take {@code numMappings} or {@code numElements} as a parameter for
|
||||
* creating the collection instance (for example: {@link HashMap#newHashMap(int)}), throw
|
||||
* an {@code IllegalArgumentException} when a negative value is passed to them
|
||||
*/
|
||||
@Test(dataProvider = "negativeNumMappings")
|
||||
public void testNegativeNumMappings(final IntFunction<?> method, final String methodName) {
|
||||
assertThrows(IllegalArgumentException.class, () -> method.apply(-1));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue