| /* |
| * 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 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.Iterators; |
| import com.google.common.collect.LinkedHashMultiset; |
| import com.google.common.collect.Lists; |
| import com.google.common.collect.Maps; |
| import com.google.common.collect.Multimap; |
| import com.google.common.collect.Multiset; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map.Entry; |
| import java.util.Set; |
| |
| /** |
| * A series of tests that support asserting that collections cannot be modified, either through |
| * direct or indirect means. |
| * |
| * @author Robert Konigsberg |
| */ |
| @GwtCompatible |
| public class UnmodifiableCollectionTests { |
| |
| public static void assertMapEntryIsUnmodifiable(Entry<?, ?> entry) { |
| try { |
| entry.setValue(null); |
| fail("setValue on unmodifiable Map.Entry succeeded"); |
| } catch (UnsupportedOperationException expected) { |
| } |
| } |
| |
| /** |
| * Verifies that an Iterator is unmodifiable. |
| * |
| * <p>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. |
| * |
| * <p>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. |
| * |
| * <p>A collection is considered immutable if: |
| * |
| * <ol> |
| * <li>All its mutation methods result in UnsupportedOperationException, and do not change the |
| * underlying contents. |
| * <li>All methods that return objects that can indirectly mutate the collection throw |
| * UnsupportedOperationException when those mutators are called. |
| * </ol> |
| * |
| * @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 <E> void assertCollectionIsUnmodifiable(Collection<E> collection, E sampleElement) { |
| Collection<E> siblingCollection = new ArrayList<>(); |
| siblingCollection.add(sampleElement); |
| |
| Collection<E> copy = new ArrayList<>(); |
| // Avoid copy.addAll(collection), which runs afoul of an Android bug in older versions: |
| // http://b.android.com/72073 http://r.android.com/98929 |
| Iterators.addAll(copy, collection.iterator()); |
| |
| 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. |
| * |
| * <p>A set is considered immutable if: |
| * |
| * <ol> |
| * <li>All its mutation methods result in UnsupportedOperationException, and do not change the |
| * underlying contents. |
| * <li>All methods that return objects that can indirectly mutate the set throw |
| * UnsupportedOperationException when those mutators are called. |
| * </ol> |
| * |
| * @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 <E> void assertSetIsUnmodifiable(Set<E> set, E sampleElement) { |
| assertCollectionIsUnmodifiable(set, sampleElement); |
| } |
| |
| /** |
| * Verifies that a multiset is immutable. |
| * |
| * <p>A multiset is considered immutable if: |
| * |
| * <ol> |
| * <li>All its mutation methods result in UnsupportedOperationException, and do not change the |
| * underlying contents. |
| * <li>All methods that return objects that can indirectly mutate the multiset throw |
| * UnsupportedOperationException when those mutators are called. |
| * </ol> |
| * |
| * @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 <E> void assertMultisetIsUnmodifiable(Multiset<E> multiset, final E sampleElement) { |
| Multiset<E> 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); |
| |
| assertCollectionsAreEquivalent(multiset, copy); |
| |
| assertSetIsUnmodifiable(multiset.elementSet(), sampleElement); |
| assertCollectionsAreEquivalent(multiset, copy); |
| |
| assertSetIsUnmodifiable( |
| multiset.entrySet(), |
| new Multiset.Entry<E>() { |
| @Override |
| public int getCount() { |
| return 1; |
| } |
| |
| @Override |
| public E getElement() { |
| return sampleElement; |
| } |
| }); |
| assertCollectionsAreEquivalent(multiset, copy); |
| } |
| |
| /** |
| * Verifies that a multimap is immutable. |
| * |
| * <p>A multimap is considered immutable if: |
| * |
| * <ol> |
| * <li>All its mutation methods result in UnsupportedOperationException, and do not change the |
| * underlying contents. |
| * <li>All methods that return objects that can indirectly mutate the multimap throw |
| * UnsupportedOperationException when those mutators |
| * </ol> |
| * |
| * @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 <K, V> void assertMultimapIsUnmodifiable( |
| Multimap<K, V> multimap, final K sampleKey, final V sampleValue) { |
| List<Entry<K, V>> originalEntries = |
| Collections.unmodifiableList(Lists.newArrayList(multimap.entries())); |
| |
| assertMultimapRemainsUnmodified(multimap, originalEntries); |
| |
| Collection<V> sampleValueAsCollection = Collections.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(), Maps.immutableEntry(sampleKey, sampleValueAsCollection)); |
| |
| // Test #values() |
| |
| assertMultimapRemainsUnmodified(multimap, originalEntries); |
| if (!multimap.isEmpty()) { |
| Collection<V> values = multimap.asMap().entrySet().iterator().next().getValue(); |
| |
| assertCollectionIsUnmodifiable(values, sampleValue); |
| } |
| |
| // Test #entries() |
| assertCollectionIsUnmodifiable(multimap.entries(), Maps.immutableEntry(sampleKey, sampleValue)); |
| assertMultimapRemainsUnmodified(multimap, originalEntries); |
| |
| // Iterate over every element in the entry set |
| for (Entry<K, V> 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<V>) |
| try { |
| multimap.putAll(sampleKey, sampleValueAsCollection); |
| fail("putAll(K, Iterable) succeeded on unmodifiable multimap"); |
| } catch (UnsupportedOperationException expected) { |
| } |
| assertMultimapRemainsUnmodified(multimap, originalEntries); |
| |
| // Test #putAll(Multimap<K, V>) |
| Multimap<K, V> multimap2 = ArrayListMultimap.create(); |
| multimap2.put(sampleKey, sampleValue); |
| try { |
| multimap.putAll(multimap2); |
| fail("putAll(Multimap<K, V>) 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 <E> void assertCollectionsAreEquivalent( |
| Collection<E> expected, Collection<E> actual) { |
| assertIteratorsInOrder(expected.iterator(), actual.iterator()); |
| } |
| |
| private static <K, V> void assertMultimapRemainsUnmodified( |
| Multimap<K, V> expected, List<Entry<K, V>> actual) { |
| assertIteratorsInOrder(expected.entries().iterator(), actual.iterator()); |
| } |
| } |