| /* | |
| * Copyright 1999-2004 The Apache Software Foundation | |
| * | |
| * Licensed 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; | |
| import java.util.Collection; | |
| import java.util.HashMap; | |
| import java.util.Iterator; | |
| import java.util.Map; | |
| import java.util.Set; | |
| /** | |
| * Tests base {@link java.util.Map} methods and contracts. | |
| * <p> | |
| * The forces at work here are similar to those in {@link TestCollection}. | |
| * 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 implemenation is wierd, 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 #useDuplicateValues()} | |
| * <LI> {@link #useNullKey()} | |
| * <LI> {@link #useNullValue()} | |
| * <LI> {@link #isAddRemoveModifiable()} | |
| * <LI> {@link #isChangeable()} | |
| * </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 #collectionValues} 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, {@link 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 {@link | |
| * 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 #useDuplicateValues()} and have it return <code>false</code> | |
| * | |
| * @author Michael Smith | |
| * @author Rodney Waldhoff | |
| * @author Paul Jack | |
| * @version $Id: TestMap.java,v 1.20.2.1 2004/05/22 12:14:05 scolebourne Exp $ | |
| */ | |
| public abstract class TestMap extends TestObject{ | |
| // 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 collectionValues; | |
| /** HashMap created by reset(). */ | |
| protected Map confirmed; | |
| /** | |
| * Override if your map does not allow a <code>null</code> key. The | |
| * default implementation returns <code>true</code> | |
| **/ | |
| protected boolean useNullKey() { | |
| return true; | |
| } | |
| /** | |
| * Override if your map does not allow <code>null</code> values. The | |
| * default implementation returns <code>true</code>. | |
| **/ | |
| protected boolean useNullValue() { | |
| return true; | |
| } | |
| /** | |
| * Override if your map does not allow duplicate values. The default | |
| * implementation returns <code>true</code>. | |
| **/ | |
| protected boolean useDuplicateValues() { | |
| return true; | |
| } | |
| /** | |
| * Override if your map allows its mappings to be changed to new values. | |
| * The default implementation returns <code>true</code>. | |
| **/ | |
| protected boolean isChangeable() { | |
| return true; | |
| } | |
| /** | |
| * Override if your map does not allow add/remove modifications. The | |
| * default implementation returns <code>true</code>. | |
| **/ | |
| protected boolean isAddRemoveModifiable() { | |
| 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 #useNullKey()} returns <code>true</code>. | |
| **/ | |
| protected Object[] getSampleKeys() { | |
| Object[] result = new Object[] { | |
| "blah", "foo", "bar", "baz", "tmp", "gosh", "golly", "gee", | |
| "hello", "goodbye", "we'll", "see", "you", "all", "again", | |
| "key", | |
| "key2", | |
| (useNullKey()) ? null : "nonnullkey" | |
| }; | |
| return result; | |
| } | |
| protected Object[] getOtherKeys() { | |
| return TestCollection.getOtherNonNullStringElements(); | |
| } | |
| protected Object[] getOtherValues() { | |
| return TestCollection.getOtherNonNullStringElements(); | |
| } | |
| /** | |
| * 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 contructs a set of | |
| * String values and includes a single null value if {@link | |
| * #useNullValue()} returns <code>true</code>, and includes two values | |
| * that are the same if {@link #useDuplicateValues()} returns | |
| * <code>true</code>. | |
| **/ | |
| protected Object[] getSampleValues() { | |
| Object[] result = new Object[] { | |
| "blahv", "foov", "barv", "bazv", "tmpv", "goshv", "gollyv", "geev", | |
| "hellov", "goodbyev", "we'llv", "seev", "youv", "allv", "againv", | |
| (useNullValue()) ? null : "nonnullvalue", | |
| "value", | |
| (useDuplicateValues()) ? "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 | |
| * #useNullValue()} returns <code>true</code>, and includes two values | |
| * that are the same if {@link #useDuplicateValues()} returns | |
| * <code>true</code>. | |
| **/ | |
| protected Object[] getNewSampleValues() { | |
| Object[] result = new Object[] { | |
| (useNullValue()) ? null : "newnonnullvalue", | |
| "newvalue", | |
| (useDuplicateValues()) ? "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()}. | |
| **/ | |
| protected 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); | |
| if (keys[i] == null) { | |
| if (useNullKey()) { | |
| throw new Error("NullPointerException on null key, but " + | |
| "useNullKey is not overridden to return false.", exception); | |
| } | |
| } else if (values[i] == null) { | |
| if (useNullValue()) { | |
| throw new Error("NullPointerException on null value, but " + | |
| "useNullValue is not overridden to return false.", exception); | |
| } | |
| } else { | |
| // Unknown reason for NullPointer. | |
| throw exception; | |
| } | |
| } | |
| } | |
| assertEquals("size must reflect number of mappings added.", | |
| keys.length, m.size()); | |
| } | |
| /** | |
| * Return a new, empty {@link Map} to be used for testing. | |
| */ | |
| protected abstract Map makeEmptyMap(); | |
| protected Map makeConfirmedMap() { | |
| return new HashMap(); | |
| } | |
| /** | |
| * 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. | |
| **/ | |
| protected Map makeFullMap() { | |
| Map m = makeEmptyMap(); | |
| addSampleMappings(m); | |
| return m; | |
| } | |
| public Object makeObject() { | |
| return makeEmptyMap(); | |
| } | |
| /** | |
| * 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 | |
| * useNullKey() returns true. The values array must only have a null | |
| * value if useNullValue() is true and may only have duplicate values if | |
| * useDuplicateValues() 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 useNullKey " + | |
| "is false.", keys[i] != null || useNullKey()); | |
| assertTrue("failure in test: found null value, but useNullValue " + | |
| "is false.", values[i] != null || useNullValue()); | |
| assertTrue("failure in test: found null new value, but useNullValue " + | |
| "is false.", newValues[i] != null || useNullValue()); | |
| 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 #isAddRemoveModifiable() | |
| * 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 (!isAddRemoveModifiable()) 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(); | |
| } | |
| /** | |
| * Tests Map.put(Object, Object) | |
| **/ | |
| public void testMapPut() { | |
| if (!isAddRemoveModifiable()) return; | |
| resetEmpty(); | |
| Object[] keys = getSampleKeys(); | |
| Object[] values = getSampleValues(); | |
| Object[] newValues = getNewSampleValues(); | |
| 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])); | |
| } | |
| for(int i = 0; i < keys.length; i++) { | |
| Object o = map.put(keys[i], newValues[i]); | |
| confirmed.put(keys[i], newValues[i]); | |
| verify(); | |
| assertEquals("Second map.put should return previous value", | |
| values[i], o); | |
| assertTrue("Map should still contain key after put", | |
| map.containsKey(keys[i])); | |
| assertTrue("Map should contain new value after put", | |
| map.containsValue(newValues[i])); | |
| // if duplicates are allowed, we're not guarunteed that the value | |
| // no longer exists, so don't try checking that. | |
| if(!useDuplicateValues()) { | |
| assertTrue("Map should not contain old value after second put", | |
| !map.containsValue(values[i])); | |
| } | |
| } | |
| } | |
| /** | |
| * Tests Map.putAll(Collection) | |
| **/ | |
| public void testMapPutAll() { | |
| if (!isAddRemoveModifiable()) return; | |
| resetEmpty(); | |
| Map m2 = makeFullMap(); | |
| map.putAll(m2); | |
| confirmed.putAll(m2); | |
| verify(); | |
| resetEmpty(); | |
| m2 = new HashMap(); | |
| 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(); | |
| } | |
| /** | |
| * Tests Map.remove(Object) | |
| **/ | |
| public void testMapRemove() { | |
| if (!isAddRemoveModifiable()) 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(); | |
| } | |
| /** | |
| * 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++) { | |
| result[i] = new DefaultMapEntry(keys[i], values[i]); | |
| } | |
| return result; | |
| } | |
| class TestMapEntrySet extends TestSet { | |
| public TestMapEntrySet() { | |
| super(""); | |
| } | |
| // Have to implement manually; entrySet doesn't support addAll | |
| protected Object[] getFullElements() { | |
| Object[] k = getSampleKeys(); | |
| Object[] v = getSampleValues(); | |
| return makeEntryArray(k, v); | |
| } | |
| // Have to implement manually; entrySet doesn't support addAll | |
| protected Object[] getOtherElements() { | |
| Object[] k = getOtherKeys(); | |
| Object[] v = getOtherValues(); | |
| return makeEntryArray(k, v); | |
| } | |
| protected Set makeEmptySet() { | |
| return makeEmptyMap().entrySet(); | |
| } | |
| protected Set makeFullSet() { | |
| return makeFullMap().entrySet(); | |
| } | |
| protected boolean isAddSupported() { | |
| // Collection views don't support add operations. | |
| return false; | |
| } | |
| protected boolean isRemoveSupported() { | |
| // Entry set should only support remove if map does | |
| return isAddRemoveModifiable(); | |
| } | |
| protected void resetFull() { | |
| TestMap.this.resetFull(); | |
| collection = map.entrySet(); | |
| TestMapEntrySet.this.confirmed = TestMap.this.confirmed.entrySet(); | |
| } | |
| protected void resetEmpty() { | |
| TestMap.this.resetEmpty(); | |
| collection = map.entrySet(); | |
| TestMapEntrySet.this.confirmed = TestMap.this.confirmed.entrySet(); | |
| } | |
| protected void verify() { | |
| super.verify(); | |
| TestMap.this.verify(); | |
| } | |
| } | |
| class TestMapKeySet extends TestSet { | |
| public TestMapKeySet() { | |
| super(""); | |
| } | |
| protected Object[] getFullElements() { | |
| return getSampleKeys(); | |
| } | |
| protected Object[] getOtherElements() { | |
| return getOtherKeys(); | |
| } | |
| protected Set makeEmptySet() { | |
| return makeEmptyMap().keySet(); | |
| } | |
| protected Set makeFullSet() { | |
| return makeFullMap().keySet(); | |
| } | |
| protected boolean isAddSupported() { | |
| return false; | |
| } | |
| protected boolean isRemoveSupported() { | |
| return isAddRemoveModifiable(); | |
| } | |
| protected void resetEmpty() { | |
| TestMap.this.resetEmpty(); | |
| collection = map.keySet(); | |
| TestMapKeySet.this.confirmed = TestMap.this.confirmed.keySet(); | |
| } | |
| protected void resetFull() { | |
| TestMap.this.resetFull(); | |
| collection = map.keySet(); | |
| TestMapKeySet.this.confirmed = TestMap.this.confirmed.keySet(); | |
| } | |
| protected void verify() { | |
| super.verify(); | |
| TestMap.this.verify(); | |
| } | |
| } | |
| class TestMapValues extends TestCollection { | |
| public TestMapValues() { | |
| } | |
| protected Object[] getFullElements() { | |
| return getSampleValues(); | |
| } | |
| protected Object[] getOtherElements() { | |
| return getOtherValues(); | |
| } | |
| protected Collection makeCollection() { | |
| return makeEmptyMap().values(); | |
| } | |
| protected Collection makeFullCollection() { | |
| return makeFullMap().values(); | |
| } | |
| protected boolean isAddSupported() { | |
| return false; | |
| } | |
| protected boolean isRemoveSupported() { | |
| return isAddRemoveModifiable(); | |
| } | |
| protected boolean areEqualElementsDistinguishable() { | |
| // equal values are associated with different keys, so they are | |
| // distinguishable. | |
| return true; | |
| } | |
| protected Collection makeConfirmedCollection() { | |
| // never gets called, reset methods are overridden | |
| return null; | |
| } | |
| protected Collection makeConfirmedFullCollection() { | |
| // never gets called, reset methods are overridden | |
| return null; | |
| } | |
| protected void resetFull() { | |
| TestMap.this.resetFull(); | |
| collection = map.values(); | |
| TestMapValues.this.confirmed = TestMap.this.confirmed.values(); | |
| } | |
| protected void resetEmpty() { | |
| TestMap.this.resetEmpty(); | |
| collection = map.values(); | |
| TestMapValues.this.confirmed = TestMap.this.confirmed.values(); | |
| } | |
| protected void verify() { | |
| super.verify(); | |
| TestMap.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 #collectionValues} and {@link #confirmed} fields to empty. | |
| */ | |
| protected void resetEmpty() { | |
| this.map = makeEmptyMap(); | |
| views(); | |
| this.confirmed = makeConfirmedMap(); | |
| } | |
| /** | |
| * Resets the {@link #map}, {@link #entrySet}, {@link #keySet}, | |
| * {@link #collectionValues} and {@link #confirmed} fields to full. | |
| */ | |
| protected 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.collectionValues = 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. | |
| */ | |
| protected void verify() { | |
| verifyMap(); | |
| verifyEntrySet(); | |
| verifyKeySet(); | |
| } | |
| protected 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 rexamined 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). | |
| } | |
| protected void verifyEntrySet() { | |
| int size = confirmed.size(); | |
| boolean empty = confirmed.isEmpty(); | |
| assertEquals("entrySet should be same size as HashMap's", | |
| size, entrySet.size()); | |
| assertEquals("entrySet should be empty if HashMap is", | |
| empty, entrySet.isEmpty()); | |
| assertTrue("entrySet should contain all HashMap's elements", | |
| entrySet.containsAll(confirmed.entrySet())); | |
| assertEquals("entrySet hashCodes should be the same", | |
| confirmed.entrySet().hashCode(), entrySet.hashCode()); | |
| assertEquals("Map's entry set should still equal HashMap's", | |
| confirmed.entrySet(), entrySet); | |
| } | |
| protected void verifyKeySet() { | |
| int size = confirmed.size(); | |
| boolean empty = confirmed.isEmpty(); | |
| assertEquals("keySet should be same size as HashMap's", | |
| size, keySet.size()); | |
| assertEquals("keySet should be empty if HashMap is", | |
| empty, keySet.isEmpty()); | |
| assertTrue("keySet should contain all HashMap's elements", | |
| keySet.containsAll(confirmed.keySet())); | |
| assertEquals("keySet hashCodes should be the same", | |
| confirmed.keySet().hashCode(), keySet.hashCode()); | |
| assertEquals("Map's key set should still equal HashMap's", | |
| confirmed.keySet(), keySet); | |
| } | |
| } |