/* | |
* Licensed to the Apache Software Foundation (ASF) under one or more | |
* contributor license agreements. See the NOTICE file distributed with | |
* this work for additional information regarding copyright ownership. | |
* The ASF licenses this file to You under the Apache License, Version 2.0 | |
* (the "License"); you may not use this file except in compliance with | |
* the License. You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package org.apache.commons.collections.map; | |
import java.io.Serializable; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.HashMap; | |
import java.util.Iterator; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Set; | |
import org.apache.commons.collections.AbstractTestObject; | |
import org.apache.commons.collections.BulkTest; | |
import org.apache.commons.collections.collection.AbstractTestCollection; | |
import org.apache.commons.collections.set.AbstractTestSet; | |
/** | |
* Abstract test class for {@link java.util.Map} methods and contracts. | |
* <p> | |
* The forces at work here are similar to those in {@link AbstractTestCollection}. | |
* If your class implements the full Map interface, including optional | |
* operations, simply extend this class, and implement the | |
* {@link #makeEmptyMap()} method. | |
* <p> | |
* On the other hand, if your map implementation is weird, you may have to | |
* override one or more of the other protected methods. They're described | |
* below. | |
* <p> | |
* <b>Entry Population Methods</b> | |
* <p> | |
* Override these methods if your map requires special entries: | |
* | |
* <ul> | |
* <li>{@link #getSampleKeys()} | |
* <li>{@link #getSampleValues()} | |
* <li>{@link #getNewSampleValues()} | |
* <li>{@link #getOtherKeys()} | |
* <li>{@link #getOtherValues()} | |
* </ul> | |
* | |
* <b>Supported Operation Methods</b> | |
* <p> | |
* Override these methods if your map doesn't support certain operations: | |
* | |
* <ul> | |
* <li> {@link #isPutAddSupported()} | |
* <li> {@link #isPutChangeSupported()} | |
* <li> {@link #isSetValueSupported()} | |
* <li> {@link #isRemoveSupported()} | |
* <li> {@link #isGetStructuralModify()} | |
* <li> {@link #isAllowDuplicateValues()} | |
* <li> {@link #isAllowNullKey()} | |
* <li> {@link #isAllowNullValue()} | |
* </ul> | |
* | |
* <b>Fixture Methods</b> | |
* <p> | |
* For tests on modification operations (puts and removes), fixtures are used | |
* to verify that that operation results in correct state for the map and its | |
* collection views. Basically, the modification is performed against your | |
* map implementation, and an identical modification is performed against | |
* a <I>confirmed</I> map implementation. A confirmed map implementation is | |
* something like <Code>java.util.HashMap</Code>, which is known to conform | |
* exactly to the {@link Map} contract. After the modification takes place | |
* on both your map implementation and the confirmed map implementation, the | |
* two maps are compared to see if their state is identical. The comparison | |
* also compares the collection views to make sure they're still the same.<P> | |
* | |
* The upshot of all that is that <I>any</I> test that modifies the map in | |
* <I>any</I> way will verify that <I>all</I> of the map's state is still | |
* correct, including the state of its collection views. So for instance | |
* if a key is removed by the map's key set's iterator, then the entry set | |
* is checked to make sure the key/value pair no longer appears.<P> | |
* | |
* The {@link #map} field holds an instance of your collection implementation. | |
* The {@link #entrySet}, {@link #keySet} and {@link #values} fields hold | |
* that map's collection views. And the {@link #confirmed} field holds | |
* an instance of the confirmed collection implementation. The | |
* {@link #resetEmpty()} and {@link #resetFull()} methods set these fields to | |
* empty or full maps, so that tests can proceed from a known state.<P> | |
* | |
* After a modification operation to both {@link #map} and {@link #confirmed}, | |
* the {@link #verify()} method is invoked to compare the results. The | |
* {@link #verify} method calls separate methods to verify the map and its three | |
* collection views ({@link #verifyMap}, {@link #verifyEntrySet}, | |
* {@link #verifyKeySet}, and {@link #verifyValues}). You may want to override | |
* one of the verification methodsto perform additional verifications. For | |
* instance, TestDoubleOrderedMap would want override its | |
* {@link #verifyValues()} method to verify that the values are unique and in | |
* ascending order.<P> | |
* | |
* <b>Other Notes</b> | |
* <p> | |
* If your {@link Map} fails one of these tests by design, you may still use | |
* this base set of cases. Simply override the test case (method) your map | |
* fails and/or the methods that define the assumptions used by the test | |
* cases. For example, if your map does not allow duplicate values, override | |
* {@link #isAllowDuplicateValues()} and have it return <code>false</code> | |
* | |
* @author Michael Smith | |
* @author Rodney Waldhoff | |
* @author Paul Jack | |
* @author Stephen Colebourne | |
* @version $Revision: 646780 $ $Date: 2008-04-10 13:48:07 +0100 (Thu, 10 Apr 2008) $ | |
*/ | |
public abstract class AbstractTestMap extends AbstractTestObject { | |
/** | |
* JDK1.2 has bugs in null handling of Maps, especially HashMap.Entry.toString | |
* This avoids nulls for JDK1.2 | |
*/ | |
private static final boolean JDK12; | |
static { | |
String str = System.getProperty("java.version"); | |
JDK12 = str.startsWith("1.2"); | |
} | |
// These instance variables are initialized with the reset method. | |
// Tests for map methods that alter the map (put, putAll, remove) | |
// first call reset() to create the map and its views; then perform | |
// the modification on the map; perform the same modification on the | |
// confirmed; and then call verify() to ensure that the map is equal | |
// to the confirmed, that the already-constructed collection views | |
// are still equal to the confirmed's collection views. | |
/** Map created by reset(). */ | |
protected Map map; | |
/** Entry set of map created by reset(). */ | |
protected Set entrySet; | |
/** Key set of map created by reset(). */ | |
protected Set keySet; | |
/** Values collection of map created by reset(). */ | |
protected Collection values; | |
/** HashMap created by reset(). */ | |
protected Map confirmed; | |
/** | |
* JUnit constructor. | |
* | |
* @param testName the test name | |
*/ | |
public AbstractTestMap(String testName) { | |
super(testName); | |
} | |
/** | |
* Returns true if the maps produced by | |
* {@link #makeEmptyMap()} and {@link #makeFullMap()} | |
* support the <code>put</code> and <code>putAll</code> operations | |
* adding new mappings. | |
* <p> | |
* Default implementation returns true. | |
* Override if your collection class does not support put adding. | |
*/ | |
public boolean isPutAddSupported() { | |
return true; | |
} | |
/** | |
* Returns true if the maps produced by | |
* {@link #makeEmptyMap()} and {@link #makeFullMap()} | |
* support the <code>put</code> and <code>putAll</code> operations | |
* changing existing mappings. | |
* <p> | |
* Default implementation returns true. | |
* Override if your collection class does not support put changing. | |
*/ | |
public boolean isPutChangeSupported() { | |
return true; | |
} | |
/** | |
* Returns true if the maps produced by | |
* {@link #makeEmptyMap()} and {@link #makeFullMap()} | |
* support the <code>setValue</code> operation on entrySet entries. | |
* <p> | |
* Default implementation returns isPutChangeSupported(). | |
* Override if your collection class does not support setValue but does | |
* support put changing. | |
*/ | |
public boolean isSetValueSupported() { | |
return isPutChangeSupported(); | |
} | |
/** | |
* Returns true if the maps produced by | |
* {@link #makeEmptyMap()} and {@link #makeFullMap()} | |
* support the <code>remove</code> and <code>clear</code> operations. | |
* <p> | |
* Default implementation returns true. | |
* Override if your collection class does not support removal operations. | |
*/ | |
public boolean isRemoveSupported() { | |
return true; | |
} | |
/** | |
* Returns true if the maps produced by | |
* {@link #makeEmptyMap()} and {@link #makeFullMap()} | |
* can cause structural modification on a get(). The example is LRUMap. | |
* <p> | |
* Default implementation returns false. | |
* Override if your map class structurally modifies on get. | |
*/ | |
public boolean isGetStructuralModify() { | |
return false; | |
} | |
/** | |
* Returns whether the sub map views of SortedMap are serializable. | |
* If the class being tested is based around a TreeMap then you should | |
* override and return false as TreeMap has a bug in deserialization. | |
* | |
* @return false | |
*/ | |
public boolean isSubMapViewsSerializable() { | |
return true; | |
} | |
/** | |
* Returns true if the maps produced by | |
* {@link #makeEmptyMap()} and {@link #makeFullMap()} | |
* supports null keys. | |
* <p> | |
* Default implementation returns true. | |
* Override if your collection class does not support null keys. | |
*/ | |
public boolean isAllowNullKey() { | |
return true; | |
} | |
/** | |
* Returns true if the maps produced by | |
* {@link #makeEmptyMap()} and {@link #makeFullMap()} | |
* supports null values. | |
* <p> | |
* Default implementation returns true. | |
* Override if your collection class does not support null values. | |
*/ | |
public boolean isAllowNullValue() { | |
return true; | |
} | |
/** | |
* Returns true if the maps produced by | |
* {@link #makeEmptyMap()} and {@link #makeFullMap()} | |
* supports duplicate values. | |
* <p> | |
* Default implementation returns true. | |
* Override if your collection class does not support duplicate values. | |
*/ | |
public boolean isAllowDuplicateValues() { | |
return true; | |
} | |
/** | |
* Returns the set of keys in the mappings used to test the map. This | |
* method must return an array with the same length as {@link | |
* #getSampleValues()} and all array elements must be different. The | |
* default implementation constructs a set of String keys, and includes a | |
* single null key if {@link #isAllowNullKey()} returns <code>true</code>. | |
*/ | |
public Object[] getSampleKeys() { | |
Object[] result = new Object[] { | |
"blah", "foo", "bar", "baz", "tmp", "gosh", "golly", "gee", | |
"hello", "goodbye", "we'll", "see", "you", "all", "again", | |
"key", | |
"key2", | |
(isAllowNullKey() && !JDK12) ? null : "nonnullkey" | |
}; | |
return result; | |
} | |
public Object[] getOtherKeys() { | |
return getOtherNonNullStringElements(); | |
} | |
public Object[] getOtherValues() { | |
return getOtherNonNullStringElements(); | |
} | |
/** | |
* Returns a list of string elements suitable for return by | |
* {@link #getOtherKeys()} or {@link #getOtherValues}. | |
* | |
* <p>Override getOtherElements to returnthe results of this method if your | |
* collection does not support heterogenous elements or the null element. | |
* </p> | |
*/ | |
public Object[] getOtherNonNullStringElements() { | |
return new Object[] { | |
"For","then","despite",/* of */"space","I","would","be","brought", | |
"From","limits","far","remote","where","thou","dost","stay" | |
}; | |
} | |
/** | |
* Returns the set of values in the mappings used to test the map. This | |
* method must return an array with the same length as | |
* {@link #getSampleKeys()}. The default implementation constructs a set of | |
* String values and includes a single null value if | |
* {@link #isAllowNullValue()} returns <code>true</code>, and includes | |
* two values that are the same if {@link #isAllowDuplicateValues()} returns | |
* <code>true</code>. | |
*/ | |
public Object[] getSampleValues() { | |
Object[] result = new Object[] { | |
"blahv", "foov", "barv", "bazv", "tmpv", "goshv", "gollyv", "geev", | |
"hellov", "goodbyev", "we'llv", "seev", "youv", "allv", "againv", | |
(isAllowNullValue() && !JDK12) ? null : "nonnullvalue", | |
"value", | |
(isAllowDuplicateValues()) ? "value" : "value2", | |
}; | |
return result; | |
} | |
/** | |
* Returns a the set of values that can be used to replace the values | |
* returned from {@link #getSampleValues()}. This method must return an | |
* array with the same length as {@link #getSampleValues()}. The values | |
* returned from this method should not be the same as those returned from | |
* {@link #getSampleValues()}. The default implementation constructs a | |
* set of String values and includes a single null value if | |
* {@link #isAllowNullValue()} returns <code>true</code>, and includes two values | |
* that are the same if {@link #isAllowDuplicateValues()} returns | |
* <code>true</code>. | |
*/ | |
public Object[] getNewSampleValues() { | |
Object[] result = new Object[] { | |
(isAllowNullValue() && !JDK12 && isAllowDuplicateValues()) ? null : "newnonnullvalue", | |
"newvalue", | |
(isAllowDuplicateValues()) ? "newvalue" : "newvalue2", | |
"newblahv", "newfoov", "newbarv", "newbazv", "newtmpv", "newgoshv", | |
"newgollyv", "newgeev", "newhellov", "newgoodbyev", "newwe'llv", | |
"newseev", "newyouv", "newallv", "newagainv", | |
}; | |
return result; | |
} | |
/** | |
* Helper method to add all the mappings described by | |
* {@link #getSampleKeys()} and {@link #getSampleValues()}. | |
*/ | |
public void addSampleMappings(Map m) { | |
Object[] keys = getSampleKeys(); | |
Object[] values = getSampleValues(); | |
for(int i = 0; i < keys.length; i++) { | |
try { | |
m.put(keys[i], values[i]); | |
} catch (NullPointerException exception) { | |
assertTrue("NullPointerException only allowed to be thrown " + | |
"if either the key or value is null.", | |
keys[i] == null || values[i] == null); | |
assertTrue("NullPointerException on null key, but " + | |
"isAllowNullKey is not overridden to return false.", | |
keys[i] == null || !isAllowNullKey()); | |
assertTrue("NullPointerException on null value, but " + | |
"isAllowNullValue is not overridden to return false.", | |
values[i] == null || !isAllowNullValue()); | |
assertTrue("Unknown reason for NullPointer.", false); | |
} | |
} | |
assertEquals("size must reflect number of mappings added.", | |
keys.length, m.size()); | |
} | |
//----------------------------------------------------------------------- | |
/** | |
* Return a new, empty {@link Map} to be used for testing. | |
* | |
* @return the map to be tested | |
*/ | |
public abstract Map makeEmptyMap(); | |
/** | |
* Return a new, populated map. The mappings in the map should match the | |
* keys and values returned from {@link #getSampleKeys()} and | |
* {@link #getSampleValues()}. The default implementation uses makeEmptyMap() | |
* and calls {@link #addSampleMappings} to add all the mappings to the | |
* map. | |
* | |
* @return the map to be tested | |
*/ | |
public Map makeFullMap() { | |
Map m = makeEmptyMap(); | |
addSampleMappings(m); | |
return m; | |
} | |
/** | |
* Implements the superclass method to return the map to be tested. | |
* | |
* @return the map to be tested | |
*/ | |
public Object makeObject() { | |
return makeEmptyMap(); | |
} | |
/** | |
* Override to return a map other than HashMap as the confirmed map. | |
* | |
* @return a map that is known to be valid | |
*/ | |
public Map makeConfirmedMap() { | |
return new HashMap(); | |
} | |
/** | |
* Creates a new Map Entry that is independent of the first and the map. | |
*/ | |
public Map.Entry cloneMapEntry(Map.Entry entry) { | |
HashMap map = new HashMap(); | |
map.put(entry.getKey(), entry.getValue()); | |
return (Map.Entry) map.entrySet().iterator().next(); | |
} | |
/** | |
* Gets the compatability version, needed for package access. | |
*/ | |
public String getCompatibilityVersion() { | |
return super.getCompatibilityVersion(); | |
} | |
//----------------------------------------------------------------------- | |
/** | |
* Test to ensure the test setup is working properly. This method checks | |
* to ensure that the getSampleKeys and getSampleValues methods are | |
* returning results that look appropriate. That is, they both return a | |
* non-null array of equal length. The keys array must not have any | |
* duplicate values, and may only contain a (single) null key if | |
* isNullKeySupported() returns true. The values array must only have a null | |
* value if useNullValue() is true and may only have duplicate values if | |
* isAllowDuplicateValues() returns true. | |
*/ | |
public void testSampleMappings() { | |
Object[] keys = getSampleKeys(); | |
Object[] values = getSampleValues(); | |
Object[] newValues = getNewSampleValues(); | |
assertTrue("failure in test: Must have keys returned from " + | |
"getSampleKeys.", keys != null); | |
assertTrue("failure in test: Must have values returned from " + | |
"getSampleValues.", values != null); | |
// verify keys and values have equivalent lengths (in case getSampleX are | |
// overridden) | |
assertEquals("failure in test: not the same number of sample " + | |
"keys and values.", keys.length, values.length); | |
assertEquals("failure in test: not the same number of values and new values.", | |
values.length, newValues.length); | |
// verify there aren't duplicate keys, and check values | |
for(int i = 0; i < keys.length - 1; i++) { | |
for(int j = i + 1; j < keys.length; j++) { | |
assertTrue("failure in test: duplicate null keys.", | |
(keys[i] != null || keys[j] != null)); | |
assertTrue("failure in test: duplicate non-null key.", | |
(keys[i] == null || keys[j] == null || | |
(!keys[i].equals(keys[j]) && | |
!keys[j].equals(keys[i])))); | |
} | |
assertTrue("failure in test: found null key, but isNullKeySupported " + | |
"is false.", keys[i] != null || isAllowNullKey()); | |
assertTrue("failure in test: found null value, but isNullValueSupported " + | |
"is false.", values[i] != null || isAllowNullValue()); | |
assertTrue("failure in test: found null new value, but isNullValueSupported " + | |
"is false.", newValues[i] != null || isAllowNullValue()); | |
assertTrue("failure in test: values should not be the same as new value", | |
values[i] != newValues[i] && | |
(values[i] == null || !values[i].equals(newValues[i]))); | |
} | |
} | |
// tests begin here. Each test adds a little bit of tested functionality. | |
// Many methods assume previous methods passed. That is, they do not | |
// exhaustively recheck things that have already been checked in a previous | |
// test methods. | |
/** | |
* Test to ensure that makeEmptyMap and makeFull returns a new non-null | |
* map with each invocation. | |
*/ | |
public void testMakeMap() { | |
Map em = makeEmptyMap(); | |
assertTrue("failure in test: makeEmptyMap must return a non-null map.", | |
em != null); | |
Map em2 = makeEmptyMap(); | |
assertTrue("failure in test: makeEmptyMap must return a non-null map.", | |
em != null); | |
assertTrue("failure in test: makeEmptyMap must return a new map " + | |
"with each invocation.", em != em2); | |
Map fm = makeFullMap(); | |
assertTrue("failure in test: makeFullMap must return a non-null map.", | |
fm != null); | |
Map fm2 = makeFullMap(); | |
assertTrue("failure in test: makeFullMap must return a non-null map.", | |
fm != null); | |
assertTrue("failure in test: makeFullMap must return a new map " + | |
"with each invocation.", fm != fm2); | |
} | |
/** | |
* Tests Map.isEmpty() | |
*/ | |
public void testMapIsEmpty() { | |
resetEmpty(); | |
assertEquals("Map.isEmpty() should return true with an empty map", | |
true, map.isEmpty()); | |
verify(); | |
resetFull(); | |
assertEquals("Map.isEmpty() should return false with a non-empty map", | |
false, map.isEmpty()); | |
verify(); | |
} | |
/** | |
* Tests Map.size() | |
*/ | |
public void testMapSize() { | |
resetEmpty(); | |
assertEquals("Map.size() should be 0 with an empty map", | |
0, map.size()); | |
verify(); | |
resetFull(); | |
assertEquals("Map.size() should equal the number of entries " + | |
"in the map", getSampleKeys().length, map.size()); | |
verify(); | |
} | |
/** | |
* Tests {@link Map#clear()}. If the map {@link #isRemoveSupported()} | |
* can add and remove elements}, then {@link Map#size()} and | |
* {@link Map#isEmpty()} are used to ensure that map has no elements after | |
* a call to clear. If the map does not support adding and removing | |
* elements, this method checks to ensure clear throws an | |
* UnsupportedOperationException. | |
*/ | |
public void testMapClear() { | |
if (!isRemoveSupported()) { | |
try { | |
resetFull(); | |
map.clear(); | |
fail("Expected UnsupportedOperationException on clear"); | |
} catch (UnsupportedOperationException ex) {} | |
return; | |
} | |
resetEmpty(); | |
map.clear(); | |
confirmed.clear(); | |
verify(); | |
resetFull(); | |
map.clear(); | |
confirmed.clear(); | |
verify(); | |
} | |
/** | |
* Tests Map.containsKey(Object) by verifying it returns false for all | |
* sample keys on a map created using an empty map and returns true for | |
* all sample keys returned on a full map. | |
*/ | |
public void testMapContainsKey() { | |
Object[] keys = getSampleKeys(); | |
resetEmpty(); | |
for(int i = 0; i < keys.length; i++) { | |
assertTrue("Map must not contain key when map is empty", | |
!map.containsKey(keys[i])); | |
} | |
verify(); | |
resetFull(); | |
for(int i = 0; i < keys.length; i++) { | |
assertTrue("Map must contain key for a mapping in the map. " + | |
"Missing: " + keys[i], map.containsKey(keys[i])); | |
} | |
verify(); | |
} | |
/** | |
* Tests Map.containsValue(Object) by verifying it returns false for all | |
* sample values on an empty map and returns true for all sample values on | |
* a full map. | |
*/ | |
public void testMapContainsValue() { | |
Object[] values = getSampleValues(); | |
resetEmpty(); | |
for(int i = 0; i < values.length; i++) { | |
assertTrue("Empty map must not contain value", | |
!map.containsValue(values[i])); | |
} | |
verify(); | |
resetFull(); | |
for(int i = 0; i < values.length; i++) { | |
assertTrue("Map must contain value for a mapping in the map.", | |
map.containsValue(values[i])); | |
} | |
verify(); | |
} | |
/** | |
* Tests Map.equals(Object) | |
*/ | |
public void testMapEquals() { | |
resetEmpty(); | |
assertTrue("Empty maps unequal.", map.equals(confirmed)); | |
verify(); | |
resetFull(); | |
assertTrue("Full maps unequal.", map.equals(confirmed)); | |
verify(); | |
resetFull(); | |
// modify the HashMap created from the full map and make sure this | |
// change results in map.equals() to return false. | |
Iterator iter = confirmed.keySet().iterator(); | |
iter.next(); | |
iter.remove(); | |
assertTrue("Different maps equal.", !map.equals(confirmed)); | |
resetFull(); | |
assertTrue("equals(null) returned true.", !map.equals(null)); | |
assertTrue("equals(new Object()) returned true.", | |
!map.equals(new Object())); | |
verify(); | |
} | |
/** | |
* Tests Map.get(Object) | |
*/ | |
public void testMapGet() { | |
resetEmpty(); | |
Object[] keys = getSampleKeys(); | |
Object[] values = getSampleValues(); | |
for (int i = 0; i < keys.length; i++) { | |
assertTrue("Empty map.get() should return null.", | |
map.get(keys[i]) == null); | |
} | |
verify(); | |
resetFull(); | |
for (int i = 0; i < keys.length; i++) { | |
assertEquals("Full map.get() should return value from mapping.", | |
values[i], map.get(keys[i])); | |
} | |
} | |
/** | |
* Tests Map.hashCode() | |
*/ | |
public void testMapHashCode() { | |
resetEmpty(); | |
assertTrue("Empty maps have different hashCodes.", | |
map.hashCode() == confirmed.hashCode()); | |
resetFull(); | |
assertTrue("Equal maps have different hashCodes.", | |
map.hashCode() == confirmed.hashCode()); | |
} | |
/** | |
* Tests Map.toString(). Since the format of the string returned by the | |
* toString() method is not defined in the Map interface, there is no | |
* common way to test the results of the toString() method. Thereforce, | |
* it is encouraged that Map implementations override this test with one | |
* that checks the format matches any format defined in its API. This | |
* default implementation just verifies that the toString() method does | |
* not return null. | |
*/ | |
public void testMapToString() { | |
resetEmpty(); | |
assertTrue("Empty map toString() should not return null", | |
map.toString() != null); | |
verify(); | |
resetFull(); | |
assertTrue("Empty map toString() should not return null", | |
map.toString() != null); | |
verify(); | |
} | |
/** | |
* Compare the current serialized form of the Map | |
* against the canonical version in CVS. | |
*/ | |
public void testEmptyMapCompatibility() throws Exception { | |
/** | |
* Create canonical objects with this code | |
Map map = makeEmptyMap(); | |
if (!(map instanceof Serializable)) return; | |
writeExternalFormToDisk((Serializable) map, getCanonicalEmptyCollectionName(map)); | |
*/ | |
// test to make sure the canonical form has been preserved | |
Map map = makeEmptyMap(); | |
if (map instanceof Serializable && !skipSerializedCanonicalTests() && isTestSerialization()) { | |
Map map2 = (Map) readExternalFormFromDisk(getCanonicalEmptyCollectionName(map)); | |
assertEquals("Map is empty", 0, map2.size()); | |
} | |
} | |
/** | |
* Compare the current serialized form of the Map | |
* against the canonical version in CVS. | |
*/ | |
public void testFullMapCompatibility() throws Exception { | |
/** | |
* Create canonical objects with this code | |
Map map = makeFullMap(); | |
if (!(map instanceof Serializable)) return; | |
writeExternalFormToDisk((Serializable) map, getCanonicalFullCollectionName(map)); | |
*/ | |
// test to make sure the canonical form has been preserved | |
Map map = makeFullMap(); | |
if (map instanceof Serializable && !skipSerializedCanonicalTests() && isTestSerialization()) { | |
Map map2 = (Map) readExternalFormFromDisk(getCanonicalFullCollectionName(map)); | |
assertEquals("Map is the right size", getSampleKeys().length, map2.size()); | |
} | |
} | |
/** | |
* Tests Map.put(Object, Object) | |
*/ | |
public void testMapPut() { | |
resetEmpty(); | |
Object[] keys = getSampleKeys(); | |
Object[] values = getSampleValues(); | |
Object[] newValues = getNewSampleValues(); | |
if (isPutAddSupported()) { | |
for (int i = 0; i < keys.length; i++) { | |
Object o = map.put(keys[i], values[i]); | |
confirmed.put(keys[i], values[i]); | |
verify(); | |
assertTrue("First map.put should return null", o == null); | |
assertTrue("Map should contain key after put", | |
map.containsKey(keys[i])); | |
assertTrue("Map should contain value after put", | |
map.containsValue(values[i])); | |
} | |
if (isPutChangeSupported()) { | |
for (int i = 0; i < keys.length; i++) { | |
Object o = map.put(keys[i], newValues[i]); | |
confirmed.put(keys[i], newValues[i]); | |
verify(); | |
assertEquals("Map.put should return previous value when changed", | |
values[i], o); | |
assertTrue("Map should still contain key after put when changed", | |
map.containsKey(keys[i])); | |
assertTrue("Map should contain new value after put when changed", | |
map.containsValue(newValues[i])); | |
// if duplicates are allowed, we're not guaranteed that the value | |
// no longer exists, so don't try checking that. | |
if (!isAllowDuplicateValues()) { | |
assertTrue("Map should not contain old value after put when changed", | |
!map.containsValue(values[i])); | |
} | |
} | |
} else { | |
try { | |
// two possible exception here, either valid | |
map.put(keys[0], newValues[0]); | |
fail("Expected IllegalArgumentException or UnsupportedOperationException on put (change)"); | |
} catch (IllegalArgumentException ex) { | |
} catch (UnsupportedOperationException ex) {} | |
} | |
} else if (isPutChangeSupported()) { | |
resetEmpty(); | |
try { | |
map.put(keys[0], values[0]); | |
fail("Expected UnsupportedOperationException or IllegalArgumentException on put (add) when fixed size"); | |
} catch (IllegalArgumentException ex) { | |
} catch (UnsupportedOperationException ex) { | |
} | |
resetFull(); | |
int i = 0; | |
for (Iterator it = map.keySet().iterator(); it.hasNext() && i < newValues.length; i++) { | |
Object key = it.next(); | |
Object o = map.put(key, newValues[i]); | |
Object value = confirmed.put(key, newValues[i]); | |
verify(); | |
assertEquals("Map.put should return previous value when changed", | |
value, o); | |
assertTrue("Map should still contain key after put when changed", | |
map.containsKey(key)); | |
assertTrue("Map should contain new value after put when changed", | |
map.containsValue(newValues[i])); | |
// if duplicates are allowed, we're not guaranteed that the value | |
// no longer exists, so don't try checking that. | |
if (!isAllowDuplicateValues()) { | |
assertTrue("Map should not contain old value after put when changed", | |
!map.containsValue(values[i])); | |
} | |
} | |
} else { | |
try { | |
map.put(keys[0], values[0]); | |
fail("Expected UnsupportedOperationException on put (add)"); | |
} catch (UnsupportedOperationException ex) {} | |
} | |
} | |
/** | |
* Tests Map.put(null, value) | |
*/ | |
public void testMapPutNullKey() { | |
resetFull(); | |
Object[] values = getSampleValues(); | |
if (isPutAddSupported()) { | |
if (isAllowNullKey()) { | |
map.put(null, values[0]); | |
} else { | |
try { | |
map.put(null, values[0]); | |
fail("put(null, value) should throw NPE/IAE"); | |
} catch (NullPointerException ex) { | |
} catch (IllegalArgumentException ex) {} | |
} | |
} | |
} | |
/** | |
* Tests Map.put(null, value) | |
*/ | |
public void testMapPutNullValue() { | |
resetFull(); | |
Object[] keys = getSampleKeys(); | |
if (isPutAddSupported()) { | |
if (isAllowNullValue()) { | |
map.put(keys[0], null); | |
} else { | |
try { | |
map.put(keys[0], null); | |
fail("put(key, null) should throw NPE/IAE"); | |
} catch (NullPointerException ex) { | |
} catch (IllegalArgumentException ex) {} | |
} | |
} | |
} | |
/** | |
* Tests Map.putAll(map) | |
*/ | |
public void testMapPutAll() { | |
if (!isPutAddSupported()) { | |
if (!isPutChangeSupported()) { | |
Map temp = makeFullMap(); | |
resetEmpty(); | |
try { | |
map.putAll(temp); | |
fail("Expected UnsupportedOperationException on putAll"); | |
} catch (UnsupportedOperationException ex) {} | |
} | |
return; | |
} | |
// check putAll OK adding empty map to empty map | |
resetEmpty(); | |
assertEquals(0, map.size()); | |
map.putAll(new HashMap()); | |
assertEquals(0, map.size()); | |
// check putAll OK adding empty map to non-empty map | |
resetFull(); | |
int size = map.size(); | |
map.putAll(new HashMap()); | |
assertEquals(size, map.size()); | |
// check putAll OK adding non-empty map to empty map | |
resetEmpty(); | |
Map m2 = makeFullMap(); | |
map.putAll(m2); | |
confirmed.putAll(m2); | |
verify(); | |
// check putAll OK adding non-empty JDK map to empty map | |
resetEmpty(); | |
m2 = makeConfirmedMap(); | |
Object[] keys = getSampleKeys(); | |
Object[] values = getSampleValues(); | |
for(int i = 0; i < keys.length; i++) { | |
m2.put(keys[i], values[i]); | |
} | |
map.putAll(m2); | |
confirmed.putAll(m2); | |
verify(); | |
// check putAll OK adding non-empty JDK map to non-empty map | |
resetEmpty(); | |
m2 = makeConfirmedMap(); | |
map.put(keys[0], values[0]); | |
confirmed.put(keys[0], values[0]); | |
verify(); | |
for(int i = 1; i < keys.length; i++) { | |
m2.put(keys[i], values[i]); | |
} | |
map.putAll(m2); | |
confirmed.putAll(m2); | |
verify(); | |
} | |
/** | |
* Tests Map.remove(Object) | |
*/ | |
public void testMapRemove() { | |
if (!isRemoveSupported()) { | |
try { | |
resetFull(); | |
map.remove(map.keySet().iterator().next()); | |
fail("Expected UnsupportedOperationException on remove"); | |
} catch (UnsupportedOperationException ex) {} | |
return; | |
} | |
resetEmpty(); | |
Object[] keys = getSampleKeys(); | |
Object[] values = getSampleValues(); | |
for(int i = 0; i < keys.length; i++) { | |
Object o = map.remove(keys[i]); | |
assertTrue("First map.remove should return null", o == null); | |
} | |
verify(); | |
resetFull(); | |
for(int i = 0; i < keys.length; i++) { | |
Object o = map.remove(keys[i]); | |
confirmed.remove(keys[i]); | |
verify(); | |
assertEquals("map.remove with valid key should return value", | |
values[i], o); | |
} | |
Object[] other = getOtherKeys(); | |
resetFull(); | |
int size = map.size(); | |
for (int i = 0; i < other.length; i++) { | |
Object o = map.remove(other[i]); | |
assertEquals("map.remove for nonexistent key should return null", | |
o, null); | |
assertEquals("map.remove for nonexistent key should not " + | |
"shrink map", size, map.size()); | |
} | |
verify(); | |
} | |
//----------------------------------------------------------------------- | |
/** | |
* Tests that the {@link Map#values} collection is backed by | |
* the underlying map for clear(). | |
*/ | |
public void testValuesClearChangesMap() { | |
if (!isRemoveSupported()) return; | |
// clear values, reflected in map | |
resetFull(); | |
Collection values = map.values(); | |
assertTrue(map.size() > 0); | |
assertTrue(values.size() > 0); | |
values.clear(); | |
assertTrue(map.size() == 0); | |
assertTrue(values.size() == 0); | |
// clear map, reflected in values | |
resetFull(); | |
values = map.values(); | |
assertTrue(map.size() > 0); | |
assertTrue(values.size() > 0); | |
map.clear(); | |
assertTrue(map.size() == 0); | |
assertTrue(values.size() == 0); | |
} | |
/** | |
* Tests that the {@link Map#keySet} collection is backed by | |
* the underlying map for clear(). | |
*/ | |
public void testKeySetClearChangesMap() { | |
if (!isRemoveSupported()) return; | |
// clear values, reflected in map | |
resetFull(); | |
Set keySet = map.keySet(); | |
assertTrue(map.size() > 0); | |
assertTrue(keySet.size() > 0); | |
keySet.clear(); | |
assertTrue(map.size() == 0); | |
assertTrue(keySet.size() == 0); | |
// clear map, reflected in values | |
resetFull(); | |
keySet = map.keySet(); | |
assertTrue(map.size() > 0); | |
assertTrue(keySet.size() > 0); | |
map.clear(); | |
assertTrue(map.size() == 0); | |
assertTrue(keySet.size() == 0); | |
} | |
/** | |
* Tests that the {@link Map#entrySet()} collection is backed by | |
* the underlying map for clear(). | |
*/ | |
public void testEntrySetClearChangesMap() { | |
if (!isRemoveSupported()) return; | |
// clear values, reflected in map | |
resetFull(); | |
Set entrySet = map.entrySet(); | |
assertTrue(map.size() > 0); | |
assertTrue(entrySet.size() > 0); | |
entrySet.clear(); | |
assertTrue(map.size() == 0); | |
assertTrue(entrySet.size() == 0); | |
// clear map, reflected in values | |
resetFull(); | |
entrySet = map.entrySet(); | |
assertTrue(map.size() > 0); | |
assertTrue(entrySet.size() > 0); | |
map.clear(); | |
assertTrue(map.size() == 0); | |
assertTrue(entrySet.size() == 0); | |
} | |
//----------------------------------------------------------------------- | |
public void testEntrySetContains1() { | |
resetFull(); | |
Set entrySet = map.entrySet(); | |
Map.Entry entry = (Map.Entry) entrySet.iterator().next(); | |
assertEquals(true, entrySet.contains(entry)); | |
} | |
public void testEntrySetContains2() { | |
resetFull(); | |
Set entrySet = map.entrySet(); | |
Map.Entry entry = (Map.Entry) entrySet.iterator().next(); | |
Map.Entry test = cloneMapEntry(entry); | |
assertEquals(true, entrySet.contains(test)); | |
} | |
public void testEntrySetContains3() { | |
resetFull(); | |
Set entrySet = map.entrySet(); | |
Map.Entry entry = (Map.Entry) entrySet.iterator().next(); | |
HashMap temp = new HashMap(); | |
temp.put(entry.getKey(), "A VERY DIFFERENT VALUE"); | |
Map.Entry test = (Map.Entry) temp.entrySet().iterator().next(); | |
assertEquals(false, entrySet.contains(test)); | |
} | |
public void testEntrySetRemove1() { | |
if (!isRemoveSupported()) return; | |
resetFull(); | |
int size = map.size(); | |
Set entrySet = map.entrySet(); | |
Map.Entry entry = (Map.Entry) entrySet.iterator().next(); | |
Object key = entry.getKey(); | |
assertEquals(true, entrySet.remove(entry)); | |
assertEquals(false, map.containsKey(key)); | |
assertEquals(size - 1, map.size()); | |
} | |
public void testEntrySetRemove2() { | |
if (!isRemoveSupported()) return; | |
resetFull(); | |
int size = map.size(); | |
Set entrySet = map.entrySet(); | |
Map.Entry entry = (Map.Entry) entrySet.iterator().next(); | |
Object key = entry.getKey(); | |
Map.Entry test = cloneMapEntry(entry); | |
assertEquals(true, entrySet.remove(test)); | |
assertEquals(false, map.containsKey(key)); | |
assertEquals(size - 1, map.size()); | |
} | |
public void testEntrySetRemove3() { | |
if (!isRemoveSupported()) return; | |
resetFull(); | |
int size = map.size(); | |
Set entrySet = map.entrySet(); | |
Map.Entry entry = (Map.Entry) entrySet.iterator().next(); | |
Object key = entry.getKey(); | |
HashMap temp = new HashMap(); | |
temp.put(entry.getKey(), "A VERY DIFFERENT VALUE"); | |
Map.Entry test = (Map.Entry) temp.entrySet().iterator().next(); | |
assertEquals(false, entrySet.remove(test)); | |
assertEquals(true, map.containsKey(key)); | |
assertEquals(size, map.size()); | |
} | |
//----------------------------------------------------------------------- | |
/** | |
* Tests that the {@link Map#values} collection is backed by | |
* the underlying map by removing from the values collection | |
* and testing if the value was removed from the map. | |
* <p> | |
* We should really test the "vice versa" case--that values removed | |
* from the map are removed from the values collection--also, | |
* but that's a more difficult test to construct (lacking a | |
* "removeValue" method.) | |
* </p> | |
* <p> | |
* See bug <a href="http://issues.apache.org/bugzilla/show_bug.cgi?id=9573"> | |
* 9573</a>. | |
* </p> | |
*/ | |
public void testValuesRemoveChangesMap() { | |
resetFull(); | |
Object[] sampleValues = getSampleValues(); | |
Collection values = map.values(); | |
for (int i = 0; i < sampleValues.length; i++) { | |
if (map.containsValue(sampleValues[i])) { | |
int j = 0; // loop counter prevents infinite loops when remove is broken | |
while (values.contains(sampleValues[i]) && j < 10000) { | |
try { | |
values.remove(sampleValues[i]); | |
} catch (UnsupportedOperationException e) { | |
// if values.remove is unsupported, just skip this test | |
return; | |
} | |
j++; | |
} | |
assertTrue("values().remove(obj) is broken", j < 10000); | |
assertTrue( | |
"Value should have been removed from the underlying map.", | |
!map.containsValue(sampleValues[i])); | |
} | |
} | |
} | |
/** | |
* Tests that the {@link Map#keySet} set is backed by | |
* the underlying map by removing from the keySet set | |
* and testing if the key was removed from the map. | |
*/ | |
public void testKeySetRemoveChangesMap() { | |
resetFull(); | |
Object[] sampleKeys = getSampleKeys(); | |
Set keys = map.keySet(); | |
for (int i = 0; i < sampleKeys.length; i++) { | |
try { | |
keys.remove(sampleKeys[i]); | |
} catch (UnsupportedOperationException e) { | |
// if key.remove is unsupported, just skip this test | |
return; | |
} | |
assertTrue( | |
"Key should have been removed from the underlying map.", | |
!map.containsKey(sampleKeys[i])); | |
} | |
} | |
// TODO: Need: | |
// testValuesRemovedFromEntrySetAreRemovedFromMap | |
// same for EntrySet/KeySet/values's | |
// Iterator.remove, removeAll, retainAll | |
/** | |
* Utility methods to create an array of Map.Entry objects | |
* out of the given key and value arrays.<P> | |
* | |
* @param keys the array of keys | |
* @param values the array of values | |
* @return an array of Map.Entry of those keys to those values | |
*/ | |
private Map.Entry[] makeEntryArray(Object[] keys, Object[] values) { | |
Map.Entry[] result = new Map.Entry[keys.length]; | |
for (int i = 0; i < keys.length; i++) { | |
Map map = makeConfirmedMap(); | |
map.put(keys[i], values[i]); | |
result[i] = (Map.Entry) map.entrySet().iterator().next(); | |
} | |
return result; | |
} | |
/** | |
* Bulk test {@link Map#entrySet()}. This method runs through all of | |
* the tests in {@link AbstractTestSet}. | |
* After modification operations, {@link #verify()} is invoked to ensure | |
* that the map and the other collection views are still valid. | |
* | |
* @return a {@link AbstractTestSet} instance for testing the map's entry set | |
*/ | |
public BulkTest bulkTestMapEntrySet() { | |
return new TestMapEntrySet(); | |
} | |
public class TestMapEntrySet extends AbstractTestSet { | |
public TestMapEntrySet() { | |
super("MapEntrySet"); | |
} | |
// Have to implement manually; entrySet doesn't support addAll | |
public Object[] getFullElements() { | |
Object[] k = getSampleKeys(); | |
Object[] v = getSampleValues(); | |
return makeEntryArray(k, v); | |
} | |
// Have to implement manually; entrySet doesn't support addAll | |
public Object[] getOtherElements() { | |
Object[] k = getOtherKeys(); | |
Object[] v = getOtherValues(); | |
return makeEntryArray(k, v); | |
} | |
public Set makeEmptySet() { | |
return makeEmptyMap().entrySet(); | |
} | |
public Set makeFullSet() { | |
return makeFullMap().entrySet(); | |
} | |
public boolean isAddSupported() { | |
// Collection views don't support add operations. | |
return false; | |
} | |
public boolean isRemoveSupported() { | |
// Entry set should only support remove if map does | |
return AbstractTestMap.this.isRemoveSupported(); | |
} | |
public boolean isGetStructuralModify() { | |
return AbstractTestMap.this.isGetStructuralModify(); | |
} | |
public boolean isTestSerialization() { | |
return false; | |
} | |
public void resetFull() { | |
AbstractTestMap.this.resetFull(); | |
collection = map.entrySet(); | |
TestMapEntrySet.this.confirmed = AbstractTestMap.this.confirmed.entrySet(); | |
} | |
public void resetEmpty() { | |
AbstractTestMap.this.resetEmpty(); | |
collection = map.entrySet(); | |
TestMapEntrySet.this.confirmed = AbstractTestMap.this.confirmed.entrySet(); | |
} | |
public void testMapEntrySetIteratorEntry() { | |
resetFull(); | |
Iterator it = collection.iterator(); | |
int count = 0; | |
while (it.hasNext()) { | |
Map.Entry entry = (Map.Entry) it.next(); | |
assertEquals(true, AbstractTestMap.this.map.containsKey(entry.getKey())); | |
assertEquals(true, AbstractTestMap.this.map.containsValue(entry.getValue())); | |
if (isGetStructuralModify() == false) { | |
assertEquals(AbstractTestMap.this.map.get(entry.getKey()), entry.getValue()); | |
} | |
count++; | |
} | |
assertEquals(collection.size(), count); | |
} | |
public void testMapEntrySetIteratorEntrySetValue() { | |
Object key1 = getSampleKeys()[0]; | |
Object key2 = (getSampleKeys().length ==1 ? getSampleKeys()[0] : getSampleKeys()[1]); | |
Object newValue1 = getNewSampleValues()[0]; | |
Object newValue2 = (getNewSampleValues().length ==1 ? getNewSampleValues()[0] : getNewSampleValues()[1]); | |
resetFull(); | |
// explicitly get entries as sample values/keys are connected for some maps | |
// such as BeanMap | |
Iterator it = TestMapEntrySet.this.collection.iterator(); | |
Map.Entry entry1 = getEntry(it, key1); | |
it = TestMapEntrySet.this.collection.iterator(); | |
Map.Entry entry2 = getEntry(it, key2); | |
Iterator itConfirmed = TestMapEntrySet.this.confirmed.iterator(); | |
Map.Entry entryConfirmed1 = getEntry(itConfirmed, key1); | |
itConfirmed = TestMapEntrySet.this.confirmed.iterator(); | |
Map.Entry entryConfirmed2 = getEntry(itConfirmed, key2); | |
verify(); | |
if (isSetValueSupported() == false) { | |
try { | |
entry1.setValue(newValue1); | |
} catch (UnsupportedOperationException ex) { | |
} | |
return; | |
} | |
entry1.setValue(newValue1); | |
entryConfirmed1.setValue(newValue1); | |
assertEquals(newValue1, entry1.getValue()); | |
assertEquals(true, AbstractTestMap.this.map.containsKey(entry1.getKey())); | |
assertEquals(true, AbstractTestMap.this.map.containsValue(newValue1)); | |
assertEquals(newValue1, AbstractTestMap.this.map.get(entry1.getKey())); | |
verify(); | |
entry1.setValue(newValue1); | |
entryConfirmed1.setValue(newValue1); | |
assertEquals(newValue1, entry1.getValue()); | |
assertEquals(true, AbstractTestMap.this.map.containsKey(entry1.getKey())); | |
assertEquals(true, AbstractTestMap.this.map.containsValue(newValue1)); | |
assertEquals(newValue1, AbstractTestMap.this.map.get(entry1.getKey())); | |
verify(); | |
entry2.setValue(newValue2); | |
entryConfirmed2.setValue(newValue2); | |
assertEquals(newValue2, entry2.getValue()); | |
assertEquals(true, AbstractTestMap.this.map.containsKey(entry2.getKey())); | |
assertEquals(true, AbstractTestMap.this.map.containsValue(newValue2)); | |
assertEquals(newValue2, AbstractTestMap.this.map.get(entry2.getKey())); | |
verify(); | |
} | |
public Map.Entry getEntry(Iterator itConfirmed, Object key) { | |
Map.Entry entry = null; | |
while (itConfirmed.hasNext()) { | |
Map.Entry temp = (Map.Entry) itConfirmed.next(); | |
if (temp.getKey() == null) { | |
if (key == null) { | |
entry = temp; | |
break; | |
} | |
} else if (temp.getKey().equals(key)) { | |
entry = temp; | |
break; | |
} | |
} | |
assertNotNull("No matching entry in map for key '" + key + "'", entry); | |
return entry; | |
} | |
public void testMapEntrySetRemoveNonMapEntry() { | |
if (isRemoveSupported() == false) return; | |
resetFull(); | |
assertEquals(false, getSet().remove(null)); | |
assertEquals(false, getSet().remove(new Object())); | |
} | |
public void verify() { | |
super.verify(); | |
AbstractTestMap.this.verify(); | |
} | |
} | |
/** | |
* Bulk test {@link Map#keySet()}. This method runs through all of | |
* the tests in {@link AbstractTestSet}. | |
* After modification operations, {@link #verify()} is invoked to ensure | |
* that the map and the other collection views are still valid. | |
* | |
* @return a {@link AbstractTestSet} instance for testing the map's key set | |
*/ | |
public BulkTest bulkTestMapKeySet() { | |
return new TestMapKeySet(); | |
} | |
public class TestMapKeySet extends AbstractTestSet { | |
public TestMapKeySet() { | |
super(""); | |
} | |
public Object[] getFullElements() { | |
return getSampleKeys(); | |
} | |
public Object[] getOtherElements() { | |
return getOtherKeys(); | |
} | |
public Set makeEmptySet() { | |
return makeEmptyMap().keySet(); | |
} | |
public Set makeFullSet() { | |
return makeFullMap().keySet(); | |
} | |
public boolean isNullSupported() { | |
return AbstractTestMap.this.isAllowNullKey(); | |
} | |
public boolean isAddSupported() { | |
return false; | |
} | |
public boolean isRemoveSupported() { | |
return AbstractTestMap.this.isRemoveSupported(); | |
} | |
public boolean isTestSerialization() { | |
return false; | |
} | |
public void resetEmpty() { | |
AbstractTestMap.this.resetEmpty(); | |
collection = map.keySet(); | |
TestMapKeySet.this.confirmed = AbstractTestMap.this.confirmed.keySet(); | |
} | |
public void resetFull() { | |
AbstractTestMap.this.resetFull(); | |
collection = map.keySet(); | |
TestMapKeySet.this.confirmed = AbstractTestMap.this.confirmed.keySet(); | |
} | |
public void verify() { | |
super.verify(); | |
AbstractTestMap.this.verify(); | |
} | |
} | |
/** | |
* Bulk test {@link Map#values()}. This method runs through all of | |
* the tests in {@link AbstractTestCollection}. | |
* After modification operations, {@link #verify()} is invoked to ensure | |
* that the map and the other collection views are still valid. | |
* | |
* @return a {@link AbstractTestCollection} instance for testing the map's | |
* values collection | |
*/ | |
public BulkTest bulkTestMapValues() { | |
return new TestMapValues(); | |
} | |
public class TestMapValues extends AbstractTestCollection { | |
public TestMapValues() { | |
super(""); | |
} | |
public Object[] getFullElements() { | |
return getSampleValues(); | |
} | |
public Object[] getOtherElements() { | |
return getOtherValues(); | |
} | |
public Collection makeCollection() { | |
return makeEmptyMap().values(); | |
} | |
public Collection makeFullCollection() { | |
return makeFullMap().values(); | |
} | |
public boolean isNullSupported() { | |
return AbstractTestMap.this.isAllowNullKey(); | |
} | |
public boolean isAddSupported() { | |
return false; | |
} | |
public boolean isRemoveSupported() { | |
return AbstractTestMap.this.isRemoveSupported(); | |
} | |
public boolean isTestSerialization() { | |
return false; | |
} | |
public boolean areEqualElementsDistinguishable() { | |
// equal values are associated with different keys, so they are | |
// distinguishable. | |
return true; | |
} | |
public Collection makeConfirmedCollection() { | |
// never gets called, reset methods are overridden | |
return null; | |
} | |
public Collection makeConfirmedFullCollection() { | |
// never gets called, reset methods are overridden | |
return null; | |
} | |
public void resetFull() { | |
AbstractTestMap.this.resetFull(); | |
collection = map.values(); | |
TestMapValues.this.confirmed = AbstractTestMap.this.confirmed.values(); | |
} | |
public void resetEmpty() { | |
AbstractTestMap.this.resetEmpty(); | |
collection = map.values(); | |
TestMapValues.this.confirmed = AbstractTestMap.this.confirmed.values(); | |
} | |
public void verify() { | |
super.verify(); | |
AbstractTestMap.this.verify(); | |
} | |
// TODO: should test that a remove on the values collection view | |
// removes the proper mapping and not just any mapping that may have | |
// the value equal to the value returned from the values iterator. | |
} | |
/** | |
* Resets the {@link #map}, {@link #entrySet}, {@link #keySet}, | |
* {@link #values} and {@link #confirmed} fields to empty. | |
*/ | |
public void resetEmpty() { | |
this.map = makeEmptyMap(); | |
views(); | |
this.confirmed = makeConfirmedMap(); | |
} | |
/** | |
* Resets the {@link #map}, {@link #entrySet}, {@link #keySet}, | |
* {@link #values} and {@link #confirmed} fields to full. | |
*/ | |
public void resetFull() { | |
this.map = makeFullMap(); | |
views(); | |
this.confirmed = makeConfirmedMap(); | |
Object[] k = getSampleKeys(); | |
Object[] v = getSampleValues(); | |
for (int i = 0; i < k.length; i++) { | |
confirmed.put(k[i], v[i]); | |
} | |
} | |
/** | |
* Resets the collection view fields. | |
*/ | |
private void views() { | |
this.keySet = map.keySet(); | |
this.values = map.values(); | |
this.entrySet = map.entrySet(); | |
} | |
/** | |
* Verifies that {@link #map} is still equal to {@link #confirmed}. | |
* This method checks that the map is equal to the HashMap, | |
* <I>and</I> that the map's collection views are still equal to | |
* the HashMap's collection views. An <Code>equals</Code> test | |
* is done on the maps and their collection views; their size and | |
* <Code>isEmpty</Code> results are compared; their hashCodes are | |
* compared; and <Code>containsAll</Code> tests are run on the | |
* collection views. | |
*/ | |
public void verify() { | |
verifyMap(); | |
verifyEntrySet(); | |
verifyKeySet(); | |
verifyValues(); | |
} | |
public void verifyMap() { | |
int size = confirmed.size(); | |
boolean empty = confirmed.isEmpty(); | |
assertEquals("Map should be same size as HashMap", | |
size, map.size()); | |
assertEquals("Map should be empty if HashMap is", | |
empty, map.isEmpty()); | |
assertEquals("hashCodes should be the same", | |
confirmed.hashCode(), map.hashCode()); | |
// this fails for LRUMap because confirmed.equals() somehow modifies | |
// map, causing concurrent modification exceptions. | |
//assertEquals("Map should still equal HashMap", confirmed, map); | |
// this works though and performs the same verification: | |
assertTrue("Map should still equal HashMap", map.equals(confirmed)); | |
// TODO: this should really be reexamined to figure out why LRU map | |
// behaves like it does (the equals shouldn't modify since all accesses | |
// by the confirmed collection should be through an iterator, thus not | |
// causing LRUMap to change). | |
} | |
public void verifyEntrySet() { | |
int size = confirmed.size(); | |
boolean empty = confirmed.isEmpty(); | |
assertEquals("entrySet should be same size as HashMap's" + | |
"\nTest: " + entrySet + "\nReal: " + confirmed.entrySet(), | |
size, entrySet.size()); | |
assertEquals("entrySet should be empty if HashMap is" + | |
"\nTest: " + entrySet + "\nReal: " + confirmed.entrySet(), | |
empty, entrySet.isEmpty()); | |
assertTrue("entrySet should contain all HashMap's elements" + | |
"\nTest: " + entrySet + "\nReal: " + confirmed.entrySet(), | |
entrySet.containsAll(confirmed.entrySet())); | |
assertEquals("entrySet hashCodes should be the same" + | |
"\nTest: " + entrySet + "\nReal: " + confirmed.entrySet(), | |
confirmed.entrySet().hashCode(), entrySet.hashCode()); | |
assertEquals("Map's entry set should still equal HashMap's", | |
confirmed.entrySet(), entrySet); | |
} | |
public void verifyKeySet() { | |
int size = confirmed.size(); | |
boolean empty = confirmed.isEmpty(); | |
assertEquals("keySet should be same size as HashMap's" + | |
"\nTest: " + keySet + "\nReal: " + confirmed.keySet(), | |
size, keySet.size()); | |
assertEquals("keySet should be empty if HashMap is" + | |
"\nTest: " + keySet + "\nReal: " + confirmed.keySet(), | |
empty, keySet.isEmpty()); | |
assertTrue("keySet should contain all HashMap's elements" + | |
"\nTest: " + keySet + "\nReal: " + confirmed.keySet(), | |
keySet.containsAll(confirmed.keySet())); | |
assertEquals("keySet hashCodes should be the same" + | |
"\nTest: " + keySet + "\nReal: " + confirmed.keySet(), | |
confirmed.keySet().hashCode(), keySet.hashCode()); | |
assertEquals("Map's key set should still equal HashMap's", | |
confirmed.keySet(), keySet); | |
} | |
public void verifyValues() { | |
List known = new ArrayList(confirmed.values()); | |
List test = new ArrayList(values); | |
int size = confirmed.size(); | |
boolean empty = confirmed.isEmpty(); | |
assertEquals("values should be same size as HashMap's" + | |
"\nTest: " + test + "\nReal: " + known, | |
size, values.size()); | |
assertEquals("values should be empty if HashMap is" + | |
"\nTest: " + test + "\nReal: " + known, | |
empty, values.isEmpty()); | |
assertTrue("values should contain all HashMap's elements" + | |
"\nTest: " + test + "\nReal: " + known, | |
test.containsAll(known)); | |
assertTrue("values should contain all HashMap's elements" + | |
"\nTest: " + test + "\nReal: " + known, | |
known.containsAll(test)); | |
// originally coded to use a HashBag, but now separate jar so... | |
for (Iterator it = known.iterator(); it.hasNext();) { | |
boolean removed = test.remove(it.next()); | |
assertTrue("Map's values should still equal HashMap's", removed); | |
} | |
assertTrue("Map's values should still equal HashMap's", test.isEmpty()); | |
} | |
/** | |
* Erases any leftover instance variables by setting them to null. | |
*/ | |
public void tearDown() throws Exception { | |
map = null; | |
keySet = null; | |
entrySet = null; | |
values = null; | |
confirmed = null; | |
} | |
} |