blob: e4b7e69b0cc0ec0b6a12ff10c7582d32a9ad2cfd [file] [log] [blame]
/*
* 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.collections4.multimap;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.commons.collections4.AbstractObjectTest;
import org.apache.commons.collections4.Bag;
import org.apache.commons.collections4.BulkTest;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapIterator;
import org.apache.commons.collections4.MultiSet;
import org.apache.commons.collections4.MultiValuedMap;
import org.apache.commons.collections4.SetValuedMap;
import org.apache.commons.collections4.bag.AbstractBagTest;
import org.apache.commons.collections4.bag.HashBag;
import org.apache.commons.collections4.collection.AbstractCollectionTest;
import org.apache.commons.collections4.map.AbstractMapTest;
import org.apache.commons.collections4.multiset.AbstractMultiSetTest;
import org.apache.commons.collections4.set.AbstractSetTest;
import org.junit.jupiter.api.Test;
/**
* Abstract test class for {@link MultiValuedMap} contract and methods.
* <p>
* To use, extend this class and implement the {@link #makeObject} method and if
* necessary override the {@link #makeFullMap()} method.
*
* @since 4.1
*/
public abstract class AbstractMultiValuedMapTest<K, V> extends AbstractObjectTest {
/** Map created by reset(). */
protected MultiValuedMap<K, V> map;
/** MultiValuedHashMap created by reset(). */
protected MultiValuedMap<K, V> confirmed;
public AbstractMultiValuedMapTest(final String testName) {
super(testName);
}
@Override
public abstract MultiValuedMap<K, V> makeObject();
@Override
public String getCompatibilityVersion() {
return "4.1"; // MultiValuedMap has been added in version 4.1
}
/**
* Returns true if the maps produced by {@link #makeObject()} and
* {@link #makeFullMap()} support the {@code put} and
* {@code putAll} operations adding new mappings.
* <p>
* Default implementation returns true. Override if your collection class
* does not support put adding.
*/
public boolean isAddSupported() {
return true;
}
/**
* Returns true if the maps produced by {@link #makeObject()} and
* {@link #makeFullMap()} support the {@code remove} and
* {@code clear} 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 #makeObject()} 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 #makeObject()} and
* {@link #makeFullMap()} supports set value.
* <p>
* Default implementation returns false. Override if your collection class
* supports set value.
*/
public boolean isHashSetValue() {
return false;
}
@Override
public boolean isTestSerialization() {
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}.
*/
@SuppressWarnings("unchecked")
public K[] getSampleKeys() {
final Object[] result = {
"one", "one", "two", "two",
"three", "three"
};
return (K[]) result;
}
/**
* 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
*/
@SuppressWarnings("unchecked")
public V[] getSampleValues() {
final Object[] result = {
"uno", "un", "dos", "deux",
"tres", "trois"
};
return (V[]) result;
}
protected MultiValuedMap<K, V> makeFullMap() {
final MultiValuedMap<K, V> map = makeObject();
addSampleMappings(map);
return map;
}
protected void addSampleMappings(final MultiValuedMap<? super K, ? super V> map) {
final K[] keys = getSampleKeys();
final V[] values = getSampleValues();
for (int i = 0; i < keys.length; i++) {
map.put(keys[i], values[i]);
}
}
/**
* Override to return a MultiValuedMap other than ArrayListValuedHashMap
* as the confirmed map.
*
* @return a MultiValuedMap that is known to be valid
*/
public MultiValuedMap<K, V> makeConfirmedMap() {
return new ArrayListValuedHashMap<>();
}
public MultiValuedMap<K, V> getConfirmed() {
return this.confirmed;
}
public void setConfirmed(final MultiValuedMap<K, V> map) {
this.confirmed = map;
}
public MultiValuedMap<K, V> getMap() {
return this.map;
}
/**
* Resets the {@link #map} and {@link #confirmed} fields to empty.
*/
public void resetEmpty() {
this.map = makeObject();
this.confirmed = makeConfirmedMap();
}
/**
* Resets the {@link #map} and {@link #confirmed} fields to full.
*/
public void resetFull() {
this.map = makeFullMap();
this.confirmed = makeConfirmedMap();
final K[] k = getSampleKeys();
final V[] v = getSampleValues();
for (int i = 0; i < k.length; i++) {
confirmed.put(k[i], v[i]);
}
}
/**
* Return a flag specifying the iteration behaviour of the map.
* This is used to change the assertions used by specific tests.
* The default implementation returns 0 which indicates ordered iteration behaviour.
*
* @return the iteration behaviour
* @see AbstractCollectionTest#UNORDERED
*/
protected int getIterationBehaviour() {
return 0;
}
@Test
@SuppressWarnings("unchecked")
public void testNoMappingReturnsEmptyCol() {
final MultiValuedMap<K, V> map = makeFullMap();
assertTrue(map.get((K) "whatever").isEmpty());
}
@Test
public void testMultipleValues() {
final MultiValuedMap<K, V> map = makeFullMap();
@SuppressWarnings("unchecked")
final Collection<V> col = map.get((K) "one");
assertTrue(col.contains("uno"));
assertTrue(col.contains("un"));
}
@Test
@SuppressWarnings("unchecked")
public void testGet() {
final MultiValuedMap<K, V> map = makeFullMap();
assertTrue(map.get((K) "one").contains("uno"));
assertTrue(map.get((K) "one").contains("un"));
assertTrue(map.get((K) "two").contains("dos"));
assertTrue(map.get((K) "two").contains("deux"));
assertTrue(map.get((K) "three").contains("tres"));
assertTrue(map.get((K) "three").contains("trois"));
}
@Test
@SuppressWarnings("unchecked")
public void testAddMappingThroughGet(){
if (!isAddSupported()) {
return;
}
resetEmpty();
final MultiValuedMap<K, V> map = getMap();
final Collection<V> col1 = map.get((K) "one");
final Collection<V> col2 = map.get((K) "one");
assertTrue(col1.isEmpty());
assertTrue(col2.isEmpty());
assertEquals(0, map.size());
col1.add((V) "uno");
col2.add((V) "un");
assertTrue(map.containsKey("one"));
assertTrue(map.containsMapping("one", "uno"));
assertTrue(map.containsMapping("one", "un"));
assertTrue(map.containsValue("uno"));
assertTrue(map.containsValue("un"));
assertTrue(col1.contains("un"));
assertTrue(col2.contains("uno"));
}
@Test
public void testRemoveMappingThroughGet() {
if (!isRemoveSupported()) {
return;
}
resetFull();
final MultiValuedMap<K, V> map = getMap();
@SuppressWarnings("unchecked")
Collection<V> col = map.get((K) "one");
assertEquals(2, col.size());
assertEquals(6, map.size());
col.remove("uno");
col.remove("un");
assertFalse(map.containsKey("one"));
assertFalse(map.containsMapping("one", "uno"));
assertFalse(map.containsMapping("one", "un"));
assertFalse(map.containsValue("uno"));
assertFalse(map.containsValue("un"));
assertEquals(4, map.size());
col = map.remove("one");
assertNotNull(col);
assertEquals(0, col.size());
}
@Test
public void testRemoveMappingThroughGetIterator() {
if (!isRemoveSupported()) {
return;
}
resetFull();
final MultiValuedMap<K, V> map = getMap();
@SuppressWarnings("unchecked")
final Iterator<V> it = map.get((K) "one").iterator();
while (it.hasNext()) {
it.next();
it.remove();
}
assertFalse(map.containsKey("one"));
assertFalse(map.containsMapping("one", "uno"));
assertFalse(map.containsMapping("one", "un"));
assertFalse(map.containsValue("uno"));
assertFalse(map.containsValue("un"));
assertEquals(4, map.size());
final Collection<V> coll = map.remove("one");
assertNotNull(coll);
assertEquals(0, coll.size());
}
@Test
public void testContainsValue() {
final MultiValuedMap<K, V> map = makeFullMap();
assertTrue(map.containsValue("uno"));
assertTrue(map.containsValue("un"));
assertTrue(map.containsValue("dos"));
assertTrue(map.containsValue("deux"));
assertTrue(map.containsValue("tres"));
assertTrue(map.containsValue("trois"));
assertFalse(map.containsValue("quatro"));
}
@Test
public void testKeyContainsValue() {
final MultiValuedMap<K, V> map = makeFullMap();
assertTrue(map.containsMapping("one", "uno"));
assertTrue(map.containsMapping("one", "un"));
assertTrue(map.containsMapping("two", "dos"));
assertTrue(map.containsMapping("two", "deux"));
assertTrue(map.containsMapping("three", "tres"));
assertTrue(map.containsMapping("three", "trois"));
assertFalse(map.containsMapping("four", "quatro"));
}
@Test
@SuppressWarnings("unchecked")
public void testValues() {
final MultiValuedMap<K, V> map = makeFullMap();
final HashSet<V> expected = new HashSet<>();
expected.add((V) "uno");
expected.add((V) "dos");
expected.add((V) "tres");
expected.add((V) "un");
expected.add((V) "deux");
expected.add((V) "trois");
final Collection<V> c = map.values();
assertEquals(6, c.size());
assertEquals(expected, new HashSet<>(c));
}
// public void testKeyedIterator() {
// final MultiValuedMap<K, V> map = makeFullMap();
// final ArrayList<Object> actual = new ArrayList<Object>(IteratorUtils.toList(map.iterator("one")));
// final ArrayList<Object> expected = new ArrayList<Object>(Arrays.asList("uno", "un"));
// assertEquals(expected, actual);
// }
@Test
@SuppressWarnings("unchecked")
public void testRemoveAllViaValuesIterator() {
if (!isRemoveSupported()) {
return;
}
final MultiValuedMap<K, V> map = makeFullMap();
for (final Iterator<?> i = map.values().iterator(); i.hasNext();) {
i.next();
i.remove();
}
assertTrue(map.get((K) "one").isEmpty());
assertTrue(map.isEmpty());
}
@Test
public void testRemoveViaValuesRemove() {
if (!isRemoveSupported()) {
return;
}
final MultiValuedMap<K, V> map = makeFullMap();
final Collection<V> values = map.values();
values.remove("uno");
values.remove("un");
assertFalse(map.containsKey("one"));
assertEquals(4, map.size());
}
/*public void testRemoveViaGetCollectionRemove() {
if (!isRemoveSupported()) {
return;
}
final MultiValuedMap<K, V> map = makeFullMap();
Collection<V> values = map.get("one");
values.remove("uno");
values.remove("un");
assertFalse(map.containsKey("one"));
assertEquals(4, map.size());
}*/
// public void testRemoveAllViaKeyedIterator() {
// if (!isRemoveSupported()) {
// return;
// }
// final MultiValuedMap<K, V> map = makeFullMap();
// for (final Iterator<?> i = map.iterator("one"); i.hasNext();) {
// i.next();
// i.remove();
// }
// assertNull(map.get("one"));
// assertEquals(4, map.size());
// }
@Test
public void testEntriesCollectionIterator() {
final MultiValuedMap<K, V> map = makeFullMap();
final Collection<V> values = new ArrayList<>(map.values());
final Iterator<Map.Entry<K, V>> iterator = map.entries().iterator();
while (iterator.hasNext()) {
final Map.Entry<K, V> entry = iterator.next();
assertTrue(map.containsMapping(entry.getKey(), entry.getValue()));
assertTrue(values.contains(entry.getValue()));
if (isRemoveSupported()) {
assertTrue(values.remove(entry.getValue()));
}
}
if (isRemoveSupported()) {
assertTrue(values.isEmpty());
}
}
@Test
@SuppressWarnings("unchecked")
public void testRemoveAllViaEntriesIterator() {
if (!isRemoveSupported()) {
return;
}
final MultiValuedMap<K, V> map = makeFullMap();
for (final Iterator<?> i = map.entries().iterator(); i.hasNext();) {
i.next();
i.remove();
}
assertTrue(map.get((K) "one").isEmpty());
assertEquals(0, map.size());
}
@Test
public void testSize() {
assertEquals(6, makeFullMap().size());
}
@Test
@SuppressWarnings("unchecked")
public void testMapEquals() {
if (!isAddSupported()) {
return;
}
final MultiValuedMap<K, V> one = makeObject();
final Integer value = Integer.valueOf(1);
one.put((K) "One", (V) value);
one.removeMapping("One", value);
final MultiValuedMap<K, V> two = makeObject();
assertEquals(two, one);
}
@Test
@SuppressWarnings("unchecked")
public void testSizeWithPutRemove() {
if (!isRemoveSupported() || !isAddSupported()) {
return;
}
final MultiValuedMap<K, V> map = makeObject();
assertEquals(0, map.size());
map.put((K) "A", (V) "AA");
assertEquals(1, map.size());
map.put((K) "B", (V) "BA");
assertEquals(2, map.size());
map.put((K) "B", (V) "BB");
assertEquals(3, map.size());
map.put((K) "B", (V) "BC");
assertEquals(4, map.size());
map.remove("A");
assertEquals(3, map.size());
map.removeMapping("B", "BC");
assertEquals(2, map.size());
}
@Test
public void testKeySetSize() {
final MultiValuedMap<K, V> map = makeFullMap();
assertEquals(3, map.keySet().size());
}
@Test
@SuppressWarnings("unchecked")
public void testSize_Key() {
final MultiValuedMap<K, V> map = makeFullMap();
assertEquals(2, map.get((K) "one").size());
assertEquals(2, map.get((K) "two").size());
assertEquals(2, map.get((K) "three").size());
if (!isAddSupported()) {
return;
}
map.put((K) "A", (V) "AA");
assertEquals(1, map.get((K) "A").size());
//assertEquals(0, map.get("B").size());
map.put((K) "B", (V) "BA");
assertEquals(1, map.get((K) "A").size());
assertEquals(1, map.get((K) "B").size());
map.put((K) "B", (V) "BB");
assertEquals(1, map.get((K) "A").size());
assertEquals(2, map.get((K) "B").size());
map.put((K) "B", (V) "BC");
assertEquals(1, map.get((K) "A").size());
assertEquals(3, map.get((K) "B").size());
if (!isRemoveSupported()) {
return;
}
map.remove("A");
//assertEquals(0, map.get("A").size());
assertEquals(3, map.get((K) "B").size());
map.removeMapping("B", "BC");
//assertEquals(0, map.get("A").size());
assertEquals(2, map.get((K) "B").size());
}
// @SuppressWarnings("unchecked")
// public void testIterator_Key() {
// final MultiValuedMap<K, V> map = makeFullMap();
// Iterator<V> it = map.iterator("one");
// assertEquals(true, it.hasNext());
// Set<V> values = new HashSet<V>();
// while (it.hasNext()) {
// values.add(it.next());
// }
// assertEquals(true, values.contains("un"));
// assertEquals(true, values.contains("uno"));
// assertEquals(false, map.iterator("A").hasNext());
// assertEquals(false, map.iterator("A").hasNext());
// if (!isAddSupported()) {
// return;
// }
// map.put((K) "A", (V) "AA");
// it = map.iterator("A");
// assertEquals(true, it.hasNext());
// it.next();
// assertEquals(false, it.hasNext());
// }
@Test
@SuppressWarnings("unchecked")
public void testContainsValue_Key() {
final MultiValuedMap<K, V> map = makeFullMap();
assertTrue(map.containsMapping("one", "uno"));
assertFalse(map.containsMapping("two", "2"));
if (!isAddSupported()) {
return;
}
map.put((K) "A", (V) "AA");
assertTrue(map.containsMapping("A", "AA"));
assertFalse(map.containsMapping("A", "AB"));
}
@Test
@SuppressWarnings("unchecked")
public void testPutAll_Map1() {
if (!isAddSupported()) {
return;
}
final MultiValuedMap<K, V> original = makeObject();
original.put((K) "key", (V) "object1");
original.put((K) "key", (V) "object2");
final MultiValuedMap<K, V> test = makeObject();
test.put((K) "keyA", (V) "objectA");
test.put((K) "key", (V) "object0");
test.putAll(original);
final MultiValuedMap<K, V> originalNull = null;
assertThrows(NullPointerException.class, () -> test.putAll(originalNull),
"expecting NullPointerException");
assertEquals(2, test.keySet().size());
assertEquals(4, test.size());
assertEquals(1, test.get((K) "keyA").size());
assertEquals(3, test.get((K) "key").size());
assertTrue(test.containsValue("objectA"));
assertTrue(test.containsValue("object0"));
assertTrue(test.containsValue("object1"));
assertTrue(test.containsValue("object2"));
}
@Test
@SuppressWarnings("unchecked")
public void testPutAll_Map2() {
if (!isAddSupported()) {
return;
}
final Map<K, V> original = new HashMap<>();
original.put((K) "keyX", (V) "object1");
original.put((K) "keyY", (V) "object2");
final MultiValuedMap<K, V> test = makeObject();
test.put((K) "keyA", (V) "objectA");
test.put((K) "keyX", (V) "object0");
test.putAll(original);
final Map<K, V> originalNull = null;
assertThrows(NullPointerException.class, () -> test.putAll(originalNull),
"expecting NullPointerException");
assertEquals(3, test.keySet().size());
assertEquals(4, test.size());
assertEquals(1, test.get((K) "keyA").size());
assertEquals(2, test.get((K) "keyX").size());
assertEquals(1, test.get((K) "keyY").size());
assertTrue(test.containsValue("objectA"));
assertTrue(test.containsValue("object0"));
assertTrue(test.containsValue("object1"));
assertTrue(test.containsValue("object2"));
}
@Test
@SuppressWarnings("unchecked")
public void testPutAll_KeyIterable() {
if (!isAddSupported()) {
return;
}
final MultiValuedMap<K, V> map = makeObject();
Collection<V> coll = (Collection<V>) Arrays.asList("X", "Y", "Z");
assertTrue(map.putAll((K) "A", coll));
assertEquals(3, map.get((K) "A").size());
assertTrue(map.containsMapping("A", "X"));
assertTrue(map.containsMapping("A", "Y"));
assertTrue(map.containsMapping("A", "Z"));
assertThrows(NullPointerException.class, () -> map.putAll((K) "A", null),
"expecting NullPointerException");
assertEquals(3, map.get((K) "A").size());
assertTrue(map.containsMapping("A", "X"));
assertTrue(map.containsMapping("A", "Y"));
assertTrue(map.containsMapping("A", "Z"));
assertFalse(map.putAll((K) "A", new ArrayList<V>()));
assertEquals(3, map.get((K) "A").size());
assertTrue(map.containsMapping("A", "X"));
assertTrue(map.containsMapping("A", "Y"));
assertTrue(map.containsMapping("A", "Z"));
coll = (Collection<V>) Arrays.asList("M");
assertTrue(map.putAll((K) "A", coll));
assertEquals(4, map.get((K) "A").size());
assertTrue(map.containsMapping("A", "X"));
assertTrue(map.containsMapping("A", "Y"));
assertTrue(map.containsMapping("A", "Z"));
assertTrue(map.containsMapping("A", "M"));
}
@Test
@SuppressWarnings("unchecked")
public void testRemove_KeyItem() {
if (!isRemoveSupported() || !isAddSupported()) {
return;
}
final MultiValuedMap<K, V> map = makeObject();
map.put((K) "A", (V) "AA");
map.put((K) "A", (V) "AB");
map.put((K) "A", (V) "AC");
assertFalse(map.removeMapping("C", "CA"));
assertFalse(map.removeMapping("A", "AD"));
assertTrue(map.removeMapping("A", "AC"));
assertTrue(map.removeMapping("A", "AB"));
assertTrue(map.removeMapping("A", "AA"));
//assertEquals(new MultiValuedHashMap<K, V>(), map);
}
@Test
public void testToString(){
if (!isAddSupported()) {
return;
}
final MultiValuedMap<K, V> map = makeObject();
map.put((K) "A", (V) "X");
map.put((K) "A", (V) "Y");
map.put((K) "A", (V) "Z");
map.put((K) "B", (V) "U");
map.put((K) "B", (V) "V");
map.put((K) "B", (V) "W");
assertTrue(
"{A=[X, Y, Z], B=[U, V, W]}".equals(map.toString()) ||
"{B=[U, V, W], A=[X, Y, Z]}".equals(map.toString())
);
final MultiValuedMap<K, V> originalNull = null;
assertThrows(NullPointerException.class, () -> map.putAll(originalNull),
"expecting NullPointerException");
assertTrue(
"{A=[X, Y, Z], B=[U, V, W]}".equals(map.toString()) ||
"{B=[U, V, W], A=[X, Y, Z]}".equals(map.toString())
);
map.remove("A");
map.remove("B");
assertEquals("{}", map.toString());
}
@Test
public void testKeysMultiSet() {
final MultiValuedMap<K, V> map = makeFullMap();
final MultiSet<K> keyMultiSet = map.keys();
assertEquals(2, keyMultiSet.getCount("one"));
assertEquals(2, keyMultiSet.getCount("two"));
assertEquals(2, keyMultiSet.getCount("three"));
assertEquals(0, keyMultiSet.getCount("conut"));
assertEquals(6, keyMultiSet.size());
}
@Test
public void testKeysBagIterator() {
final MultiValuedMap<K, V> map = makeFullMap();
final Collection<K> col = new ArrayList<>();
final Iterator<K> it = map.keys().iterator();
while (it.hasNext()) {
col.add(it.next());
}
final Bag<K> bag = new HashBag<>(col);
assertEquals(2, bag.getCount("one"));
assertEquals(2, bag.getCount("two"));
assertEquals(2, bag.getCount("three"));
assertEquals(6, bag.size());
}
@Test
@SuppressWarnings("unchecked")
public void testKeysBagContainsAll() {
final MultiValuedMap<K, V> map = makeFullMap();
final MultiSet<K> keyMultiSet = map.keys();
final Collection<K> col = (Collection<K>) Arrays.asList("one", "two", "three", "one", "two", "three");
assertTrue(keyMultiSet.containsAll(col));
}
@Test
public void testAsMapGet() {
resetEmpty();
Map<K, Collection<V>> mapCol = getMap().asMap();
assertNull(mapCol.get("one"));
assertEquals(0, mapCol.size());
resetFull();
mapCol = getMap().asMap();
final Collection<V> col = mapCol.get("one");
assertNotNull(col);
assertTrue(col.contains("un"));
assertTrue(col.contains("uno"));
}
@Test
public void testAsMapRemove() {
if (!isRemoveSupported()) {
return;
}
resetFull();
final Map<K, Collection<V>> mapCol = getMap().asMap();
mapCol.remove("one");
assertFalse(getMap().containsKey("one"));
assertEquals(4, getMap().size());
}
@Test
public void testMapIterator() {
resetEmpty();
MapIterator<K, V> mapIt = getMap().mapIterator();
assertFalse(mapIt.hasNext());
resetFull();
mapIt = getMap().mapIterator();
while (mapIt.hasNext()) {
final K key = mapIt.next();
final V value = mapIt.getValue();
assertTrue(getMap().containsMapping(key, value));
}
}
@Test
public void testMapIteratorRemove() {
if (!isRemoveSupported()) {
return;
}
resetFull();
final MapIterator<K, V> mapIt = getMap().mapIterator();
while (mapIt.hasNext()) {
mapIt.next();
mapIt.remove();
}
assertTrue(getMap().isEmpty());
}
@Test
@SuppressWarnings("unchecked")
public void testMapIteratorUnsupportedSet() {
resetFull();
final MapIterator<K, V> mapIt = getMap().mapIterator();
mapIt.next();
assertThrows(UnsupportedOperationException.class, () -> mapIt.setValue((V) "some value"));
}
@Test
public void testMultiValuedMapIterator() {
final MultiValuedMap<K, V> map = makeFullMap();
final MapIterator<K, V> it = map.mapIterator();
assertThrows(IllegalStateException.class, () -> it.getKey());
assertThrows(IllegalStateException.class, () -> it.getValue());
if (isAddSupported()) {
assertThrows(IllegalStateException.class, () -> it.setValue((V) "V"));
}
if (!isHashSetValue() && isAddSupported()) {
assertTrue(it.hasNext() );
assertEquals("one", it.next());
assertEquals("one", it.getKey());
assertEquals("uno", it.getValue());
assertEquals("one", it.next());
assertEquals("one", it.getKey());
assertEquals("un", it.getValue());
assertEquals("two", it.next());
assertEquals("two", it.getKey());
assertEquals("dos", it.getValue());
assertEquals("two", it.next());
assertEquals("two", it.getKey());
assertEquals("deux", it.getValue());
assertEquals("three", it.next());
assertEquals("three", it.getKey());
assertEquals("tres", it.getValue());
assertEquals("three", it.next());
assertEquals("three", it.getKey());
assertEquals("trois", it.getValue());
assertThrows(UnsupportedOperationException.class, () -> it.setValue((V) "threetrois"));
}
}
/**
* Manual serialization testing as this class cannot easily extend the AbstractTestMap
*/
@Test
public void testEmptyMapCompatibility() throws Exception {
final MultiValuedMap<?, ?> map = makeObject();
final MultiValuedMap<?, ?> map2 =
(MultiValuedMap<?, ?>) readExternalFormFromDisk(getCanonicalEmptyCollectionName(map));
assertEquals("Map is empty", 0, map2.size());
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Test
public void testFullMapCompatibility() throws Exception {
final MultiValuedMap map = makeFullMap();
final MultiValuedMap map2 =
(MultiValuedMap) readExternalFormFromDisk(getCanonicalFullCollectionName(map));
assertEquals("Map is the right size", map.size(), map2.size());
for (final Object key : map.keySet()) {
assertTrue("Map had inequal elements",
CollectionUtils.isEqualCollection(map.get(key), map2.get(key)));
if (isRemoveSupported()) {
map2.remove(key);
}
}
if (isRemoveSupported()) {
assertEquals("Map had extra values", 0, map2.size());
}
}
// Bulk Tests
/**
* Bulk test {@link MultiValuedMap#entries()}. This method runs through all
* of the tests in {@link AbstractCollectionTest}. After modification
* operations, {@link #verify()} is invoked to ensure that the map and the
* other collection views are still valid.
*
* @return a {@link AbstractCollectionTest} instance for testing the map's
* values collection
*/
public BulkTest bulkTestMultiValuedMapEntries() {
return new TestMultiValuedMapEntries();
}
public class TestMultiValuedMapEntries extends AbstractCollectionTest<Entry<K, V>> {
public TestMultiValuedMapEntries() {
super("");
}
@SuppressWarnings("unchecked")
@Override
public Entry<K, V>[] getFullElements() {
return makeFullMap().entries().toArray(new Entry[0]);
}
@Override
public Collection<Entry<K, V>> makeObject() {
return AbstractMultiValuedMapTest.this.makeObject().entries();
}
@Override
public Collection<Entry<K, V>> makeFullCollection() {
return AbstractMultiValuedMapTest.this.makeFullMap().entries();
}
@Override
public boolean isNullSupported() {
return AbstractMultiValuedMapTest.this.isAllowNullKey();
}
@Override
public boolean isAddSupported() {
// Add not supported in entries view
return false;
}
@Override
public boolean isRemoveSupported() {
return AbstractMultiValuedMapTest.this.isRemoveSupported();
}
@Override
public boolean isTestSerialization() {
return false;
}
@Override
public void resetFull() {
AbstractMultiValuedMapTest.this.resetFull();
setCollection(AbstractMultiValuedMapTest.this.getMap().entries());
TestMultiValuedMapEntries.this.setConfirmed(AbstractMultiValuedMapTest.this.getConfirmed().entries());
}
@Override
public void resetEmpty() {
AbstractMultiValuedMapTest.this.resetEmpty();
setCollection(AbstractMultiValuedMapTest.this.getMap().entries());
TestMultiValuedMapEntries.this.setConfirmed(AbstractMultiValuedMapTest.this.getConfirmed().entries());
}
@Override
public Collection<Entry<K, V>> makeConfirmedCollection() {
// never gets called, reset methods are overridden
return null;
}
@Override
public Collection<Entry<K, V>> makeConfirmedFullCollection() {
// never gets called, reset methods are overridden
return null;
}
@Override
protected int getIterationBehaviour() {
return AbstractMultiValuedMapTest.this.getIterationBehaviour();
}
}
/**
* Bulk test {@link MultiValuedMap#keySet()}. This method runs through all
* of the tests in {@link AbstractSetTest}. After modification operations,
* {@link #verify()} is invoked to ensure that the map and the other
* collection views are still valid.
*
* @return a {@link AbstractSetTest} instance for testing the map's key set
*/
public BulkTest bulkTestMultiValuedMapKeySet() {
return new TestMultiValuedMapKeySet();
}
public class TestMultiValuedMapKeySet extends AbstractSetTest<K> {
public TestMultiValuedMapKeySet() {
super("");
}
@SuppressWarnings("unchecked")
@Override
public K[] getFullElements() {
return (K[]) AbstractMultiValuedMapTest.this.makeFullMap().keySet().toArray();
}
@Override
public Set<K> makeObject() {
return AbstractMultiValuedMapTest.this.makeObject().keySet();
}
@Override
public Set<K> makeFullCollection() {
return AbstractMultiValuedMapTest.this.makeFullMap().keySet();
}
@Override
public boolean isNullSupported() {
return AbstractMultiValuedMapTest.this.isAllowNullKey();
}
@Override
public boolean isAddSupported() {
return false;
}
@Override
public boolean isRemoveSupported() {
return AbstractMultiValuedMapTest.this.isRemoveSupported();
}
@Override
public boolean isTestSerialization() {
return false;
}
@Override
protected int getIterationBehaviour() {
return AbstractMultiValuedMapTest.this.getIterationBehaviour();
}
}
/**
* Bulk test {@link MultiValuedMap#values()}. This method runs through all
* of the tests in {@link AbstractCollectionTest}. After modification
* operations, {@link #verify()} is invoked to ensure that the map and the
* other collection views are still valid.
*
* @return a {@link AbstractCollectionTest} instance for testing the map's
* values collection
*/
public BulkTest bulkTestMultiValuedMapValues() {
return new TestMultiValuedMapValues();
}
public class TestMultiValuedMapValues extends AbstractCollectionTest<V> {
public TestMultiValuedMapValues() {
super("");
}
@Override
public V[] getFullElements() {
return getSampleValues();
}
@Override
public Collection<V> makeObject() {
return AbstractMultiValuedMapTest.this.makeObject().values();
}
@Override
public Collection<V> makeFullCollection() {
return AbstractMultiValuedMapTest.this.makeFullMap().values();
}
@Override
public boolean isNullSupported() {
return AbstractMultiValuedMapTest.this.isAllowNullKey();
}
@Override
public boolean isAddSupported() {
return false;
}
@Override
public boolean isRemoveSupported() {
return AbstractMultiValuedMapTest.this.isRemoveSupported();
}
@Override
public boolean isTestSerialization() {
return false;
}
@Override
public void resetFull() {
AbstractMultiValuedMapTest.this.resetFull();
setCollection(AbstractMultiValuedMapTest.this.getMap().values());
TestMultiValuedMapValues.this.setConfirmed(AbstractMultiValuedMapTest.this.getConfirmed().values());
}
@Override
public void resetEmpty() {
AbstractMultiValuedMapTest.this.resetEmpty();
setCollection(AbstractMultiValuedMapTest.this.getMap().values());
TestMultiValuedMapValues.this.setConfirmed(AbstractMultiValuedMapTest.this.getConfirmed().values());
}
@Override
public Collection<V> makeConfirmedCollection() {
// never gets called, reset methods are overridden
return null;
}
@Override
public Collection<V> makeConfirmedFullCollection() {
// never gets called, reset methods are overridden
return null;
}
@Override
protected int getIterationBehaviour() {
return AbstractMultiValuedMapTest.this.getIterationBehaviour();
}
}
/**
* Bulk test {@link MultiValuedMap#keys()}. This method runs through all of
* the tests in {@link AbstractBagTest}. After modification operations,
* {@link #verify()} is invoked to ensure that the map and the other
* collection views are still valid.
*
* @return a {@link AbstractBagTest} instance for testing the map's values
* collection
*/
public BulkTest bulkTestMultiValuedMapKeys() {
return new TestMultiValuedMapKeys();
}
public class TestMultiValuedMapKeys extends AbstractMultiSetTest<K> {
public TestMultiValuedMapKeys() {
super("");
}
@Override
public K[] getFullElements() {
return getSampleKeys();
}
@Override
public MultiSet<K> makeObject() {
return AbstractMultiValuedMapTest.this.makeObject().keys();
}
@Override
public MultiSet<K> makeFullCollection() {
return AbstractMultiValuedMapTest.this.makeFullMap().keys();
}
@Override
public boolean isNullSupported() {
return AbstractMultiValuedMapTest.this.isAllowNullKey();
}
@Override
public boolean isAddSupported() {
return false;
}
@Override
public boolean isRemoveSupported() {
return false;
}
@Override
public boolean isTestSerialization() {
return false;
}
@Override
public void resetFull() {
AbstractMultiValuedMapTest.this.resetFull();
setCollection(AbstractMultiValuedMapTest.this.getMap().keys());
TestMultiValuedMapKeys.this.setConfirmed(AbstractMultiValuedMapTest.this.getConfirmed().keys());
}
@Override
public void resetEmpty() {
AbstractMultiValuedMapTest.this.resetEmpty();
setCollection(AbstractMultiValuedMapTest.this.getMap().keys());
TestMultiValuedMapKeys.this.setConfirmed(AbstractMultiValuedMapTest.this.getConfirmed().keys());
}
@Override
protected int getIterationBehaviour() {
return AbstractMultiValuedMapTest.this.getIterationBehaviour();
}
}
public BulkTest bulkTestAsMap() {
return new TestMultiValuedMapAsMap();
}
public class TestMultiValuedMapAsMap extends AbstractMapTest<K, Collection<V>> {
public TestMultiValuedMapAsMap() {
super("");
}
@Override
public Map<K, Collection<V>> makeObject() {
return AbstractMultiValuedMapTest.this.makeObject().asMap();
}
@Override
public Map<K, Collection<V>> makeFullMap() {
return AbstractMultiValuedMapTest.this.makeFullMap().asMap();
}
@Override
@SuppressWarnings("unchecked")
public K[] getSampleKeys() {
final K[] samplekeys = AbstractMultiValuedMapTest.this.getSampleKeys();
final Object[] finalKeys = new Object[3];
for (int i = 0; i < 3; i++) {
finalKeys[i] = samplekeys[i * 2];
}
return (K[]) finalKeys;
}
@Override
@SuppressWarnings("unchecked")
public Collection<V>[] getSampleValues() {
// Calling getMap() instead of makeObject() would make more sense, but due to concurrency
// issues, this may lead to intermittent issues. See COLLECTIONS-661. A better solution
// would be to re-design the tests, or add a boolean method to the parent.
final boolean isSetValuedMap = AbstractMultiValuedMapTest.this.makeObject() instanceof SetValuedMap;
final V[] sampleValues = AbstractMultiValuedMapTest.this.getSampleValues();
final Collection<V>[] colArr = new Collection[3];
for (int i = 0; i < 3; i++) {
final Collection<V> coll = Arrays.asList(sampleValues[i*2], sampleValues[i*2 + 1]);
colArr[i] = isSetValuedMap ? new HashSet<>(coll) : coll;
}
return colArr;
}
@Override
@SuppressWarnings("unchecked")
public Collection<V>[] getNewSampleValues() {
// See comment in getSampleValues() to understand why we are calling makeObject() and not
// getMap(). See COLLECTIONS-661 for more.
final boolean isSetValuedMap = AbstractMultiValuedMapTest.this.makeObject() instanceof SetValuedMap;
final Object[] sampleValues = { "ein", "ek", "zwei", "duey", "drei", "teen" };
final Collection<V>[] colArr = new Collection[3];
for (int i = 0; i < 3; i++) {
final Collection<V> coll = Arrays.asList((V) sampleValues[i * 2], (V) sampleValues[i * 2 + 1]);
colArr[i] = isSetValuedMap ? new HashSet<>(coll) : coll;
}
return colArr;
}
@Override
public boolean isAllowNullKey() {
return AbstractMultiValuedMapTest.this.isAllowNullKey();
}
@Override
public boolean isPutAddSupported() {
return false;
}
@Override
public boolean isPutChangeSupported() {
return false;
}
@Override
public boolean isRemoveSupported() {
return AbstractMultiValuedMapTest.this.isRemoveSupported();
}
@Override
public boolean areEqualElementsDistinguishable() {
// work-around for a problem with the EntrySet: the entries contain
// the wrapped collection, which will be automatically cleared
// when the associated key is removed from the map as the collection
// is not cached atm.
return true;
}
@Override
public boolean isTestSerialization() {
return false;
}
@Override
protected int getIterationBehaviour() {
return AbstractMultiValuedMapTest.this.getIterationBehaviour();
}
}
}