com.google.common.collect.testing.google.UnmodifiableCollectionTests Maven / Gradle / Ivy
Show all versions of guava-testlib Show documentation
/*
* 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.testing.google;
import static com.google.common.collect.Maps.immutableEntry;
import static java.util.Collections.singleton;
import static java.util.Collections.unmodifiableList;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertTrue;
import static junit.framework.TestCase.fail;
import com.google.common.annotations.GwtCompatible;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.LinkedHashMultiset;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multiset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* A series of tests that support asserting that collections cannot be modified, either through
* direct or indirect means.
*
* @author Robert Konigsberg
*/
@GwtCompatible
@ElementTypesAreNonnullByDefault
public class UnmodifiableCollectionTests {
public static void assertMapEntryIsUnmodifiable(Entry, ?> entry) {
try {
// fine because the call is going to fail without modifying the entry
@SuppressWarnings("unchecked")
Entry, @Nullable Object> nullableValueEntry = (Entry, @Nullable Object>) entry;
nullableValueEntry.setValue(null);
fail("setValue on unmodifiable Map.Entry succeeded");
} catch (UnsupportedOperationException expected) {
}
}
/**
* Verifies that an Iterator is unmodifiable.
*
* This test only works with iterators that iterate over a finite set.
*/
public static void assertIteratorIsUnmodifiable(Iterator> iterator) {
while (iterator.hasNext()) {
iterator.next();
try {
iterator.remove();
fail("Remove on unmodifiable iterator succeeded");
} catch (UnsupportedOperationException expected) {
}
}
}
/**
* Asserts that two iterators contain elements in tandem.
*
*
This test only works with iterators that iterate over a finite set.
*/
public static void assertIteratorsInOrder(
Iterator> expectedIterator, Iterator> actualIterator) {
int i = 0;
while (expectedIterator.hasNext()) {
Object expected = expectedIterator.next();
assertTrue(
"index " + i + " expected <" + expected + "., actual is exhausted",
actualIterator.hasNext());
Object actual = actualIterator.next();
assertEquals("index " + i, expected, actual);
i++;
}
if (actualIterator.hasNext()) {
fail("index " + i + ", expected is exhausted, actual <" + actualIterator.next() + ">");
}
}
/**
* Verifies that a collection is immutable.
*
*
A collection is considered immutable if:
*
*
* - All its mutation methods result in UnsupportedOperationException, and do not change the
* underlying contents.
*
- All methods that return objects that can indirectly mutate the collection throw
* UnsupportedOperationException when those mutators are called.
*
*
* @param collection the presumed-immutable collection
* @param sampleElement an element of the same type as that contained by {@code collection}.
* {@code collection} may or may not have {@code sampleElement} as a member.
*/
public static void assertCollectionIsUnmodifiable(
Collection collection, E sampleElement) {
Collection siblingCollection = new ArrayList<>();
siblingCollection.add(sampleElement);
Collection copy = new ArrayList<>();
copy.addAll(collection);
try {
collection.add(sampleElement);
fail("add succeeded on unmodifiable collection");
} catch (UnsupportedOperationException expected) {
}
assertCollectionsAreEquivalent(copy, collection);
try {
collection.addAll(siblingCollection);
fail("addAll succeeded on unmodifiable collection");
} catch (UnsupportedOperationException expected) {
}
assertCollectionsAreEquivalent(copy, collection);
try {
collection.clear();
fail("clear succeeded on unmodifiable collection");
} catch (UnsupportedOperationException expected) {
}
assertCollectionsAreEquivalent(copy, collection);
assertIteratorIsUnmodifiable(collection.iterator());
assertCollectionsAreEquivalent(copy, collection);
try {
collection.remove(sampleElement);
fail("remove succeeded on unmodifiable collection");
} catch (UnsupportedOperationException expected) {
}
assertCollectionsAreEquivalent(copy, collection);
try {
collection.removeAll(siblingCollection);
fail("removeAll succeeded on unmodifiable collection");
} catch (UnsupportedOperationException expected) {
}
assertCollectionsAreEquivalent(copy, collection);
try {
collection.retainAll(siblingCollection);
fail("retainAll succeeded on unmodifiable collection");
} catch (UnsupportedOperationException expected) {
}
assertCollectionsAreEquivalent(copy, collection);
}
/**
* Verifies that a set is immutable.
*
* A set is considered immutable if:
*
*
* - All its mutation methods result in UnsupportedOperationException, and do not change the
* underlying contents.
*
- All methods that return objects that can indirectly mutate the set throw
* UnsupportedOperationException when those mutators are called.
*
*
* @param set the presumed-immutable set
* @param sampleElement an element of the same type as that contained by {@code set}. {@code set}
* may or may not have {@code sampleElement} as a member.
*/
public static void assertSetIsUnmodifiable(
Set set, E sampleElement) {
assertCollectionIsUnmodifiable(set, sampleElement);
}
/**
* Verifies that a multiset is immutable.
*
* A multiset is considered immutable if:
*
*
* - All its mutation methods result in UnsupportedOperationException, and do not change the
* underlying contents.
*
- All methods that return objects that can indirectly mutate the multiset throw
* UnsupportedOperationException when those mutators are called.
*
*
* @param multiset the presumed-immutable multiset
* @param sampleElement an element of the same type as that contained by {@code multiset}. {@code
* multiset} may or may not have {@code sampleElement} as a member.
*/
public static void assertMultisetIsUnmodifiable(
Multiset multiset, E sampleElement) {
Multiset copy = LinkedHashMultiset.create(multiset);
assertCollectionsAreEquivalent(multiset, copy);
// Multiset is a collection, so we can use all those tests.
assertCollectionIsUnmodifiable(multiset, sampleElement);
assertCollectionsAreEquivalent(multiset, copy);
try {
multiset.add(sampleElement, 2);
fail("add(Object, int) succeeded on unmodifiable collection");
} catch (UnsupportedOperationException expected) {
}
assertCollectionsAreEquivalent(multiset, copy);
try {
multiset.remove(sampleElement, 2);
fail("remove(Object, int) succeeded on unmodifiable collection");
} catch (UnsupportedOperationException expected) {
}
assertCollectionsAreEquivalent(multiset, copy);
try {
multiset.removeIf(x -> false);
fail("removeIf(Predicate) succeeded on unmodifiable collection");
} catch (UnsupportedOperationException expected) {
}
assertCollectionsAreEquivalent(multiset, copy);
assertSetIsUnmodifiable(multiset.elementSet(), sampleElement);
assertCollectionsAreEquivalent(multiset, copy);
assertSetIsUnmodifiable(
multiset.entrySet(),
new Multiset.Entry() {
@Override
public int getCount() {
return 1;
}
@Override
public E getElement() {
return sampleElement;
}
});
assertCollectionsAreEquivalent(multiset, copy);
}
/**
* Verifies that a multimap is immutable.
*
* A multimap is considered immutable if:
*
*
* - All its mutation methods result in UnsupportedOperationException, and do not change the
* underlying contents.
*
- All methods that return objects that can indirectly mutate the multimap throw
* UnsupportedOperationException when those mutators
*
*
* @param multimap the presumed-immutable multimap
* @param sampleKey a key of the same type as that contained by {@code multimap}. {@code multimap}
* may or may not have {@code sampleKey} as a key.
* @param sampleValue a key of the same type as that contained by {@code multimap}. {@code
* multimap} may or may not have {@code sampleValue} as a key.
*/
public static
void assertMultimapIsUnmodifiable(Multimap multimap, K sampleKey, V sampleValue) {
List> originalEntries = unmodifiableList(Lists.newArrayList(multimap.entries()));
assertMultimapRemainsUnmodified(multimap, originalEntries);
Collection sampleValueAsCollection = singleton(sampleValue);
// Test #clear()
try {
multimap.clear();
fail("clear succeeded on unmodifiable multimap");
} catch (UnsupportedOperationException expected) {
}
assertMultimapRemainsUnmodified(multimap, originalEntries);
// Test asMap().entrySet()
assertSetIsUnmodifiable(
multimap.asMap().entrySet(), immutableEntry(sampleKey, sampleValueAsCollection));
// Test #values()
assertMultimapRemainsUnmodified(multimap, originalEntries);
if (!multimap.isEmpty()) {
Collection values = multimap.asMap().entrySet().iterator().next().getValue();
assertCollectionIsUnmodifiable(values, sampleValue);
}
// Test #entries()
assertCollectionIsUnmodifiable(multimap.entries(), immutableEntry(sampleKey, sampleValue));
assertMultimapRemainsUnmodified(multimap, originalEntries);
// Iterate over every element in the entry set
for (Entry entry : multimap.entries()) {
assertMapEntryIsUnmodifiable(entry);
}
assertMultimapRemainsUnmodified(multimap, originalEntries);
// Test #keys()
assertMultisetIsUnmodifiable(multimap.keys(), sampleKey);
assertMultimapRemainsUnmodified(multimap, originalEntries);
// Test #keySet()
assertSetIsUnmodifiable(multimap.keySet(), sampleKey);
assertMultimapRemainsUnmodified(multimap, originalEntries);
// Test #get()
if (!multimap.isEmpty()) {
K key = multimap.keySet().iterator().next();
assertCollectionIsUnmodifiable(multimap.get(key), sampleValue);
assertMultimapRemainsUnmodified(multimap, originalEntries);
}
// Test #put()
try {
multimap.put(sampleKey, sampleValue);
fail("put succeeded on unmodifiable multimap");
} catch (UnsupportedOperationException expected) {
}
assertMultimapRemainsUnmodified(multimap, originalEntries);
// Test #putAll(K, Collection)
try {
multimap.putAll(sampleKey, sampleValueAsCollection);
fail("putAll(K, Iterable) succeeded on unmodifiable multimap");
} catch (UnsupportedOperationException expected) {
}
assertMultimapRemainsUnmodified(multimap, originalEntries);
// Test #putAll(Multimap)
Multimap multimap2 = ArrayListMultimap.create();
multimap2.put(sampleKey, sampleValue);
try {
multimap.putAll(multimap2);
fail("putAll(Multimap) succeeded on unmodifiable multimap");
} catch (UnsupportedOperationException expected) {
}
assertMultimapRemainsUnmodified(multimap, originalEntries);
// Test #remove()
try {
multimap.remove(sampleKey, sampleValue);
fail("remove succeeded on unmodifiable multimap");
} catch (UnsupportedOperationException expected) {
}
assertMultimapRemainsUnmodified(multimap, originalEntries);
// Test #removeAll()
try {
multimap.removeAll(sampleKey);
fail("removeAll succeeded on unmodifiable multimap");
} catch (UnsupportedOperationException expected) {
}
assertMultimapRemainsUnmodified(multimap, originalEntries);
// Test #replaceValues()
try {
multimap.replaceValues(sampleKey, sampleValueAsCollection);
fail("replaceValues succeeded on unmodifiable multimap");
} catch (UnsupportedOperationException expected) {
}
assertMultimapRemainsUnmodified(multimap, originalEntries);
// Test #asMap()
try {
multimap.asMap().remove(sampleKey);
fail("asMap().remove() succeeded on unmodifiable multimap");
} catch (UnsupportedOperationException expected) {
}
assertMultimapRemainsUnmodified(multimap, originalEntries);
if (!multimap.isEmpty()) {
K presentKey = multimap.keySet().iterator().next();
try {
multimap.asMap().get(presentKey).remove(sampleValue);
fail("asMap().get().remove() succeeded on unmodifiable multimap");
} catch (UnsupportedOperationException expected) {
}
assertMultimapRemainsUnmodified(multimap, originalEntries);
try {
multimap.asMap().values().iterator().next().remove(sampleValue);
fail("asMap().values().iterator().next().remove() succeeded on unmodifiable multimap");
} catch (UnsupportedOperationException expected) {
}
try {
((Collection>) multimap.asMap().values().toArray()[0]).clear();
fail("asMap().values().toArray()[0].clear() succeeded on unmodifiable multimap");
} catch (UnsupportedOperationException expected) {
}
}
assertCollectionIsUnmodifiable(multimap.values(), sampleValue);
assertMultimapRemainsUnmodified(multimap, originalEntries);
}
private static void assertCollectionsAreEquivalent(
Collection expected, Collection actual) {
assertIteratorsInOrder(expected.iterator(), actual.iterator());
}
private static
void assertMultimapRemainsUnmodified(Multimap expected, List> actual) {
assertIteratorsInOrder(expected.entries().iterator(), actual.iterator());
}
}