/*
* Copyright (C) 2007 The Guava Authors
*
* 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 com.google.common.collect;
import static com.google.common.collect.testing.IteratorFeature.MODIFIABLE;
import static java.util.Arrays.asList;
import static org.junit.contrib.truth.Truth.ASSERT;
import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.GwtIncompatible;
import com.google.common.collect.testing.ListIteratorTester;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
/**
* Tests for {@code ListMultimap} implementations.
*
* @author Jared Levy
*/
@GwtCompatible(emulated = true)
public abstract class AbstractListMultimapTest extends AbstractMultimapTest {
@Override protected abstract ListMultimap<String, Integer> create();
/**
* Test adding duplicate key-value pairs to multimap.
*/
public void testDuplicates() {
Multimap<String, Integer> multimap = create();
multimap.put("foo", 1);
multimap.put("foo", 3);
multimap.put("bar", 3);
multimap.put("foo", 1);
assertEquals(4, multimap.size());
assertTrue(multimap.containsEntry("foo", 1));
multimap.remove("foo", 1);
assertEquals(3, multimap.size());
assertTrue(multimap.containsEntry("foo", 1));
}
/**
* Test returned boolean when adding duplicate key-value pairs to multimap.
*/
public void testPutReturn() {
Multimap<String, Integer> multimap = create();
assertTrue(multimap.put("foo", 1));
assertTrue(multimap.put("foo", 1));
assertTrue(multimap.put("foo", 3));
assertTrue(multimap.put("bar", 5));
}
public void testPutAllReturn_existingElements() {
Multimap<String, Integer> multimap = create();
assertTrue(multimap.putAll("foo", asList(1, 2, 3)));
assertTrue(multimap.put("foo", 1));
assertTrue(multimap.putAll("foo", asList(1, 2, 3)));
assertTrue(multimap.putAll("foo", asList(1, 3)));
Multimap<String, Integer> other = create();
other.putAll("foo", asList(1, 2));
assertTrue(multimap.putAll(other));
other.putAll("bar", asList(1, 2));
assertTrue(multimap.putAll(other));
assertTrue(other.putAll(multimap));
}
/**
* Confirm that get() returns a collection equal to a List.
*/
public void testGetEquals() {
Multimap<String, Integer> multimap = create();
multimap.put("foo", 1);
multimap.put("foo", 3);
assertEquals(ImmutableList.of(1, 3), multimap.get("foo"));
}
public void testAsMapEquals() {
Multimap<String, Integer> multimap = getMultimap();
multimap.put("foo", 1);
multimap.put("foo", nullValue());
multimap.put(nullKey(), 3);
Map<String, Collection<Integer>> map = multimap.asMap();
Map<String, Collection<Integer>> equalMap = Maps.newHashMap();
equalMap.put("foo", asList(1, nullValue()));
equalMap.put(nullKey(), asList(3));
assertEquals(map, equalMap);
assertEquals(equalMap, map);
assertEquals(equalMap.hashCode(), multimap.hashCode());
Map<String, Collection<Integer>> unequalMap = Maps.newHashMap();
equalMap.put("foo", asList(3, nullValue()));
equalMap.put(nullKey(), asList(1));
assertFalse(map.equals(unequalMap));
assertFalse(unequalMap.equals(map));
}
/**
* Confirm that asMap().entrySet() returns values equal to a List.
*/
public void testAsMapEntriesEquals() {
Multimap<String, Integer> multimap = create();
multimap.put("foo", 1);
multimap.put("foo", 3);
Iterator<Map.Entry<String, Collection<Integer>>> i =
multimap.asMap().entrySet().iterator();
Map.Entry<String, Collection<Integer>> entry = i.next();
assertEquals("foo", entry.getKey());
assertEquals(ImmutableList.of(1, 3), entry.getValue());
assertFalse(i.hasNext());
}
public void testAsMapValuesRemove() {
Multimap<String, Integer> multimap = create();
multimap.put("foo", 1);
multimap.put("foo", 3);
multimap.put("bar", 3);
Collection<Collection<Integer>> asMapValues = multimap.asMap().values();
assertFalse(asMapValues.remove(asList(3, 1)));
assertEquals(3, multimap.size());
assertTrue(asMapValues.remove(asList(1, 3)));
assertEquals(1, multimap.size());
}
/**
* Test multimap.equals() for multimaps with different insertion orderings.
*/
public void testEqualsOrdering() {
Multimap<String, Integer> multimap = create();
multimap.put("foo", 1);
multimap.put("foo", 3);
multimap.put("bar", 3);
Multimap<String, Integer> multimap2 = create();
multimap2.put("foo", 3);
multimap2.put("foo", 1);
multimap2.put("bar", 3);
assertFalse(multimap.equals(multimap2));
}
/**
* Test the ordering of the values returned by multimap.get().
*/
public void testPutGetOrdering() {
Multimap<String, Integer> multimap = create();
multimap.put("foo", 1);
multimap.put("foo", 3);
multimap.put("bar", 3);
Iterator<Integer> values = multimap.get("foo").iterator();
assertEquals(Integer.valueOf(1), values.next());
assertEquals(Integer.valueOf(3), values.next());
}
/**
* Test List-specific methods on List returned by get().
*/
public void testListMethods() {
ListMultimap<String, Integer> multimap = create();
multimap.put("foo", 1);
multimap.put("foo", 3);
multimap.put("foo", 5);
List<Integer> list = multimap.get("foo");
list.add(1, 2);
assertEquals(4, multimap.size());
ASSERT.that(multimap.get("foo")).hasContentsInOrder(1, 2, 3, 5);
list.addAll(3, asList(4, 8));
assertEquals(6, multimap.size());
ASSERT.that(multimap.get("foo")).hasContentsInOrder(1, 2, 3, 4, 8, 5);
assertEquals(8, list.get(4).intValue());
assertEquals(4, list.indexOf(8));
assertEquals(4, list.lastIndexOf(8));
list.remove(4);
assertEquals(5, multimap.size());
ASSERT.that(multimap.get("foo")).hasContentsInOrder(1, 2, 3, 4, 5);
list.set(4, 10);
assertEquals(5, multimap.size());
ASSERT.that(multimap.get("foo")).hasContentsInOrder(1, 2, 3, 4, 10);
}
public void testListMethodsIncludingSublist() {
ListMultimap<String, Integer> multimap = create();
multimap.put("foo", 1);
multimap.put("foo", 2);
multimap.put("foo", 3);
multimap.put("foo", 4);
multimap.put("foo", 10);
List<Integer> list = multimap.get("foo");
List<Integer> sublist = list.subList(1, 4);
ASSERT.that(sublist).hasContentsInOrder(2, 3, 4);
list.set(3, 6);
ASSERT.that(multimap.get("foo")).hasContentsInOrder(1, 2, 3, 6, 10);
}
/**
* Test sublist of List returned by get() after the original list is updated.
*/
public void testSublistAfterListUpdate() {
ListMultimap<String, Integer> multimap = create();
multimap.put("foo", 1);
multimap.put("foo", 2);
multimap.put("foo", 3);
multimap.put("foo", 4);
multimap.put("foo", 5);
List<Integer> list = multimap.get("foo");
List<Integer> sublist = list.subList(1, 4);
ASSERT.that(sublist).hasContentsInOrder(2, 3, 4);
list.set(3, 6);
ASSERT.that(multimap.get("foo")).hasContentsInOrder(1, 2, 3, 6, 5);
ASSERT.that(sublist).hasContentsInOrder(2, 3, 6);
}
/**
* Test ListIterator methods that don't change the multimap.
*/
public void testListIteratorNavigate() {
ListMultimap<String, Integer> multimap = create();
multimap.put("foo", 1);
multimap.put("foo", 3);
List<Integer> list = multimap.get("foo");
ListIterator<Integer> iterator = list.listIterator();
assertFalse(iterator.hasPrevious());
assertTrue(iterator.hasNext());
assertEquals(0, iterator.nextIndex());
assertEquals(-1, iterator.previousIndex());
assertEquals(1, iterator.next().intValue());
assertEquals(3, iterator.next().intValue());
assertTrue(iterator.hasPrevious());
assertFalse(iterator.hasNext());
assertEquals(3, iterator.previous().intValue());
assertEquals(1, iterator.previous().intValue());
}
/**
* Test ListIterator methods that change the multimap.
*/
public void testListIteratorUpdate() {
ListMultimap<String, Integer> multimap = create();
multimap.put("foo", 1);
multimap.put("foo", 3);
multimap.put("foo", 5);
List<Integer> list = multimap.get("foo");
ListIterator<Integer> iterator = list.listIterator();
assertEquals(1, iterator.next().intValue());
iterator.set(2);
ASSERT.that(multimap.get("foo")).hasContentsInOrder(2, 3, 5);
assertEquals(3, iterator.next().intValue());
iterator.remove();
ASSERT.that(multimap.get("foo")).hasContentsInOrder(2, 5);
}
/**
* Test calling toString() on the multimap, which does not have a
* deterministic iteration order for keys but does for values.
*/
public void testToString() {
String s = createSample().toString();
assertTrue(s.equals("{foo=[3, -1, 2, 4, 1], bar=[1, 2, 3, 1]}")
|| s.equals("{bar=[1, 2, 3, 1], foo=[3, -1, 2, 4, 1]}"));
}
/**
* Test calling set() on a sublist.
*/
public void testSublistSet() {
ListMultimap<String, Integer> multimap = create();
multimap.putAll("foo", asList(1, 2, 3, 4, 5));
List<Integer> list = multimap.get("foo");
ASSERT.that(multimap.get("foo")).hasContentsInOrder(1, 2, 3, 4, 5);
List<Integer> sublist = list.subList(1, 4);
ASSERT.that(sublist).hasContentsInOrder(2, 3, 4);
sublist.set(1, 6);
ASSERT.that(multimap.get("foo")).hasContentsInOrder(1, 2, 6, 4, 5);
}
/**
* Test removing elements from a sublist.
*/
public void testSublistRemove() {
ListMultimap<String, Integer> multimap = create();
multimap.putAll("foo", asList(1, 2, 3, 4, 5));
List<Integer> list = multimap.get("foo");
ASSERT.that(multimap.get("foo")).hasContentsInOrder(1, 2, 3, 4, 5);
List<Integer> sublist = list.subList(1, 4);
ASSERT.that(sublist).hasContentsInOrder(2, 3, 4);
sublist.remove(1);
assertEquals(4, multimap.size());
ASSERT.that(multimap.get("foo")).hasContentsInOrder(1, 2, 4, 5);
sublist.removeAll(Collections.singleton(4));
assertEquals(3, multimap.size());
ASSERT.that(multimap.get("foo")).hasContentsInOrder(1, 2, 5);
sublist.remove(0);
assertEquals(2, multimap.size());
ASSERT.that(multimap.get("foo")).hasContentsInOrder(1, 5);
}
/**
* Test adding elements to a sublist.
*/
public void testSublistAdd() {
ListMultimap<String, Integer> multimap = create();
multimap.putAll("foo", asList(1, 2, 3, 4, 5));
List<Integer> list = multimap.get("foo");
ASSERT.that(multimap.get("foo")).hasContentsInOrder(1, 2, 3, 4, 5);
List<Integer> sublist = list.subList(1, 4);
ASSERT.that(sublist).hasContentsInOrder(2, 3, 4);
sublist.add(6);
assertEquals(6, multimap.size());
ASSERT.that(multimap.get("foo")).hasContentsInOrder(1, 2, 3, 4, 6, 5);
sublist.add(0, 7);
assertEquals(7, multimap.size());
ASSERT.that(multimap.get("foo")).hasContentsInOrder(1, 7, 2, 3, 4, 6, 5);
}
/**
* Test clearing a sublist.
*/
public void testSublistClear() {
ListMultimap<String, Integer> multimap = create();
multimap.putAll("foo", asList(1, 2, 3, 4, 5));
List<Integer> list = multimap.get("foo");
ASSERT.that(multimap.get("foo")).hasContentsInOrder(1, 2, 3, 4, 5);
List<Integer> sublist = list.subList(1, 4);
ASSERT.that(sublist).hasContentsInOrder(2, 3, 4);
sublist.clear();
assertEquals(2, multimap.size());
ASSERT.that(multimap.get("foo")).hasContentsInOrder(1, 5);
}
/**
* Test adding elements to an empty sublist with an empty ancestor.
*/
public void testSublistAddToEmpty() {
ListMultimap<String, Integer> multimap = create();
multimap.putAll("foo", asList(1, 2, 3, 4, 5));
List<Integer> list = multimap.get("foo");
ASSERT.that(multimap.get("foo")).hasContentsInOrder(1, 2, 3, 4, 5);
List<Integer> sublist = list.subList(0, 5);
ASSERT.that(sublist).hasContentsInOrder(1, 2, 3, 4, 5);
sublist.retainAll(Collections.EMPTY_LIST);
assertTrue(multimap.isEmpty());
sublist.add(6);
assertEquals(1, multimap.size());
assertTrue(multimap.containsEntry("foo", 6));
}
/**
* Test updates through a list iterator retrieved by
* multimap.get(key).listIterator(index).
*/
public void testListIteratorIndexUpdate() {
ListMultimap<String, Integer> multimap = create();
multimap.putAll("foo", asList(1, 2, 3, 4, 5));
ListIterator<Integer> iterator = multimap.get("foo").listIterator(1);
assertEquals(2, iterator.next().intValue());
iterator.set(6);
ASSERT.that(multimap.get("foo")).hasContentsInOrder(1, 6, 3, 4, 5);
assertTrue(iterator.hasNext());
assertEquals(3, iterator.next().intValue());
iterator.remove();
ASSERT.that(multimap.get("foo")).hasContentsInOrder(1, 6, 4, 5);
assertEquals(4, multimap.size());
}
@GwtIncompatible("unreasonable slow")
public void testGetIteration() {
List<Integer> addItems = ImmutableList.of(99, 88, 77);
for (final int startIndex : new int[] {0, 3, 5}) {
new ListIteratorTester<Integer>(3, addItems, MODIFIABLE,
Lists.newArrayList(2, 3, 4, 7, 8), startIndex) {
private ListMultimap<String, Integer> multimap;
@Override protected ListIterator<Integer> newTargetIterator() {
multimap = create();
multimap.put("bar", 1);
multimap.putAll("foo", asList(2, 3, 4));
multimap.putAll("bar", asList(5, 6));
multimap.putAll("foo", asList(7, 8));
return multimap.get("foo").listIterator(startIndex);
}
@Override protected void verify(List<Integer> elements) {
assertEquals(elements, multimap.get("foo"));
}
}.test();
}
}
public void testListGetSet() {
ListMultimap<String, Integer> map = create();
map.put("bar", 1);
map.get("bar").set(0, 2);
assertEquals("{bar=[2]}", map.toString());
assertEquals("[bar=2]", map.entries().toString());
}
public void testListPutAllIterable() {
Multimap<String, Integer> map = create();
map.putAll("foo", asList(1, 2));
assertEquals("{foo=[1, 2]}", map.toString());
assertEquals("[foo=1, foo=2]", map.entries().toString());
}
public void testListRemoveAll() {
Multimap<String, Integer> map = create();
map.put("bar", 1);
map.put("foo", 2);
map.put("bar", 3);
map.put("bar", 4);
map.removeAll("foo");
assertEquals("[bar=1, bar=3, bar=4]", map.entries().toString());
assertEquals("{bar=[1, 3, 4]}", map.toString());
map.removeAll("bar");
assertEquals("[]", map.entries().toString());
assertEquals("{}", map.toString());
}
public void testListEquals() {
Multimap<String, Integer> map1 = create();
map1.put("bar", 1);
map1.put("foo", 2);
map1.put("bar", 3);
Multimap<String, Integer> map2 = ArrayListMultimap.create();
map2.putAll(map1);
assertTrue(map1.equals(map2));
assertTrue(map2.equals(map1));
assertFalse(map1.equals(null));
assertFalse(map1.equals(new Object()));
}
public void testListHashCode() {
Multimap<String, Integer> map1 = create();
map1.put("bar", 1);
map1.put("foo", 2);
map1.put("bar", 3);
Multimap<String, Integer> map2 = ArrayListMultimap.create();
map2.putAll(map1);
assertEquals(map1.hashCode(), map2.hashCode());
}
public void testListAddIndex() {
ListMultimap<String, Integer> multimap = create();
multimap.put("bar", 11);
multimap.put("bar", 12);
multimap.get("bar").add(0, 13);
ASSERT.that(multimap.get("bar")).hasContentsInOrder(13, 11, 12);
}
/**
* According to the AbstractCollection.retainAll() implementation,
* {@code A.retainAll(B)} should keep all occurrences of each object in B,
* so even though the collection that this test passes to retainAll() has
* fewer occurrences of 2 than the multimap has, all of the 2s should be
* retained.
*/
public void testGetRetainAll() {
// TODO: test this logic in ListRetainAllTester
ListMultimap<String, Integer> multimap = create();
multimap.putAll("foo", asList(1, 2, 2, 3, 3, 3));
multimap.get("foo").retainAll(asList(1, 2, 4));
ASSERT.that(multimap.get("foo")).hasContentsInOrder(1, 2, 2);
}
/**
* According to the AbstractCollection.removeAll() implementation,
* {@code A.removeAll(B)} should remove all occurrences of each object in B,
* so even though the collection that this test passes to removeAll() has
* fewer occurrences of 2 and 3 than the multimap has, there should be no
* 2s or 3s remaining in the collection.
*/
public void testGetRemoveAll_someValuesRemain() {
// TODO: test this logic in ListRemoveAllTester
ListMultimap<String, Integer> multimap = create();
multimap.putAll("foo", asList(1, 2, 2, 3, 3, 3));
multimap.get("foo").removeAll(asList(2, 3, 3, 4));
ASSERT.that(multimap.get("foo")).hasContentsInOrder(1);
}
}