/* * Copyright (C) 2009 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.testing.features.CollectionFeature.ALLOWS_NULL_VALUES; import static com.google.common.collect.testing.features.CollectionFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION; import static com.google.common.collect.testing.features.CollectionFeature.RESTRICTS_ELEMENTS; import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_ADD; import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_REMOVE; import static com.google.common.collect.testing.features.CollectionSize.SEVERAL; import static com.google.common.collect.testing.features.CollectionSize.ZERO; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.Multiset; import com.google.common.collect.Multiset.Entry; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import java.util.ConcurrentModificationException; import java.util.Iterator; /** * Common superclass for {@link MultisetSetCountUnconditionallyTester} and * {@link MultisetSetCountConditionallyTester}. It is used by those testers to * test calls to the unconditional {@code setCount()} method and calls to the * conditional {@code setCount()} method when the expected present count is * correct. * * @author Chris Povirk */ @GwtCompatible(emulated = true) public abstract class AbstractMultisetSetCountTester<E> extends AbstractMultisetTester<E> { /* * TODO: consider adding MultisetFeatures.SUPPORTS_SET_COUNT. Currently we * assume that using setCount() to increase the count is permitted iff add() * is permitted and similarly for decrease/remove(). We assume that a * setCount() no-op is permitted if either add() or remove() is permitted, * though we also allow it to "succeed" if neither is permitted. */ private void assertSetCount(E element, int count) { setCountCheckReturnValue(element, count); assertEquals( "multiset.count() should return the value passed to setCount()", count, getMultiset().count(element)); int size = 0; for (Multiset.Entry<E> entry : getMultiset().entrySet()) { size += entry.getCount(); } assertEquals( "multiset.size() should be the sum of the counts of all entries", size, getMultiset().size()); } /** * Call the {@code setCount()} method under test, and check its return value. */ abstract void setCountCheckReturnValue(E element, int count); /** * Call the {@code setCount()} method under test, but do not check its return * value. Callers should use this method over * {@link #setCountCheckReturnValue(Object, int)} when they expect * {@code setCount()} to throw an exception, as checking the return value * could produce an incorrect error message like * "setCount() should return the original count" instead of the message passed * to a later invocation of {@code fail()}, like "setCount should throw * UnsupportedOperationException." */ abstract void setCountNoCheckReturnValue(E element, int count); private void assertSetCountIncreasingFailure(E element, int count) { try { setCountNoCheckReturnValue(element, count); fail("a call to multiset.setCount() to increase an element's count " + "should throw"); } catch (UnsupportedOperationException expected) { } } private void assertSetCountDecreasingFailure(E element, int count) { try { setCountNoCheckReturnValue(element, count); fail("a call to multiset.setCount() to decrease an element's count " + "should throw"); } catch (UnsupportedOperationException expected) { } } // Unconditional setCount no-ops. private void assertZeroToZero() { assertSetCount(samples.e3, 0); } private void assertOneToOne() { assertSetCount(samples.e0, 1); } private void assertThreeToThree() { initThreeCopies(); assertSetCount(samples.e0, 3); } @CollectionFeature.Require(SUPPORTS_ADD) public void testSetCount_zeroToZero_addSupported() { assertZeroToZero(); } @CollectionFeature.Require(SUPPORTS_REMOVE) public void testSetCount_zeroToZero_removeSupported() { assertZeroToZero(); } @CollectionFeature.Require(absent = {SUPPORTS_ADD, SUPPORTS_REMOVE}) public void testSetCount_zeroToZero_unsupported() { try { assertZeroToZero(); } catch (UnsupportedOperationException tolerated) { } } @CollectionSize.Require(absent = ZERO) @CollectionFeature.Require(SUPPORTS_ADD) public void testSetCount_oneToOne_addSupported() { assertOneToOne(); } @CollectionSize.Require(absent = ZERO) @CollectionFeature.Require(SUPPORTS_REMOVE) public void testSetCount_oneToOne_removeSupported() { assertOneToOne(); } @CollectionSize.Require(absent = ZERO) @CollectionFeature.Require(absent = {SUPPORTS_ADD, SUPPORTS_REMOVE}) public void testSetCount_oneToOne_unsupported() { try { assertOneToOne(); } catch (UnsupportedOperationException tolerated) { } } @CollectionSize.Require(SEVERAL) @CollectionFeature.Require(SUPPORTS_ADD) public void testSetCount_threeToThree_addSupported() { assertThreeToThree(); } @CollectionSize.Require(SEVERAL) @CollectionFeature.Require(SUPPORTS_REMOVE) public void testSetCount_threeToThree_removeSupported() { assertThreeToThree(); } @CollectionSize.Require(SEVERAL) @CollectionFeature.Require(absent = {SUPPORTS_ADD, SUPPORTS_REMOVE}) public void testSetCount_threeToThree_unsupported() { try { assertThreeToThree(); } catch (UnsupportedOperationException tolerated) { } } // Unconditional setCount size increases: @CollectionFeature.Require(SUPPORTS_ADD) public void testSetCount_zeroToOne_supported() { assertSetCount(samples.e3, 1); } @CollectionFeature.Require({SUPPORTS_ADD, FAILS_FAST_ON_CONCURRENT_MODIFICATION}) public void testSetCountZeroToOneConcurrentWithIteration() { try { Iterator<E> iterator = collection.iterator(); assertSetCount(samples.e3, 1); iterator.next(); fail("Expected ConcurrentModificationException"); } catch (ConcurrentModificationException expected) { // success } } @CollectionFeature.Require({SUPPORTS_ADD, FAILS_FAST_ON_CONCURRENT_MODIFICATION}) public void testSetCountZeroToOneConcurrentWithEntrySetIteration() { try { Iterator<Entry<E>> iterator = getMultiset().entrySet().iterator(); assertSetCount(samples.e3, 1); iterator.next(); fail("Expected ConcurrentModificationException"); } catch (ConcurrentModificationException expected) { // success } } @CollectionFeature.Require(SUPPORTS_ADD) public void testSetCount_zeroToThree_supported() { assertSetCount(samples.e3, 3); } @CollectionSize.Require(absent = ZERO) @CollectionFeature.Require(SUPPORTS_ADD) public void testSetCount_oneToThree_supported() { assertSetCount(samples.e0, 3); } @CollectionFeature.Require(absent = SUPPORTS_ADD) public void testSetCount_zeroToOne_unsupported() { assertSetCountIncreasingFailure(samples.e3, 1); } @CollectionFeature.Require(absent = SUPPORTS_ADD) public void testSetCount_zeroToThree_unsupported() { assertSetCountIncreasingFailure(samples.e3, 3); } @CollectionSize.Require(absent = ZERO) @CollectionFeature.Require(absent = SUPPORTS_ADD) public void testSetCount_oneToThree_unsupported() { assertSetCountIncreasingFailure(samples.e3, 3); } // Unconditional setCount size decreases: @CollectionSize.Require(absent = ZERO) @CollectionFeature.Require(SUPPORTS_REMOVE) public void testSetCount_oneToZero_supported() { assertSetCount(samples.e0, 0); } @CollectionFeature.Require({SUPPORTS_REMOVE, FAILS_FAST_ON_CONCURRENT_MODIFICATION}) @CollectionSize.Require(absent = ZERO) public void testSetCountOneToZeroConcurrentWithIteration() { try { Iterator<E> iterator = collection.iterator(); assertSetCount(samples.e0, 0); iterator.next(); fail("Expected ConcurrentModificationException"); } catch (ConcurrentModificationException expected) { // success } } @CollectionFeature.Require({SUPPORTS_REMOVE, FAILS_FAST_ON_CONCURRENT_MODIFICATION}) public void testSetCountOneToZeroConcurrentWithEntrySetIteration() { try { Iterator<Entry<E>> iterator = getMultiset().entrySet().iterator(); assertSetCount(samples.e0, 0); iterator.next(); fail("Expected ConcurrentModificationException"); } catch (ConcurrentModificationException expected) { // success } } @CollectionSize.Require(SEVERAL) @CollectionFeature.Require(SUPPORTS_REMOVE) public void testSetCount_threeToZero_supported() { initThreeCopies(); assertSetCount(samples.e0, 0); } @CollectionSize.Require(SEVERAL) @CollectionFeature.Require(SUPPORTS_REMOVE) public void testSetCount_threeToOne_supported() { initThreeCopies(); assertSetCount(samples.e0, 1); } @CollectionSize.Require(absent = ZERO) @CollectionFeature.Require(absent = SUPPORTS_REMOVE) public void testSetCount_oneToZero_unsupported() { assertSetCountDecreasingFailure(samples.e0, 0); } @CollectionSize.Require(SEVERAL) @CollectionFeature.Require(absent = SUPPORTS_REMOVE) public void testSetCount_threeToZero_unsupported() { initThreeCopies(); assertSetCountDecreasingFailure(samples.e0, 0); } @CollectionSize.Require(SEVERAL) @CollectionFeature.Require(absent = SUPPORTS_REMOVE) public void testSetCount_threeToOne_unsupported() { initThreeCopies(); assertSetCountDecreasingFailure(samples.e0, 1); } // setCount with nulls: @CollectionSize.Require(absent = ZERO) @CollectionFeature.Require({SUPPORTS_REMOVE, ALLOWS_NULL_VALUES}) public void testSetCount_removeNull_nullSupported() { initCollectionWithNullElement(); assertSetCount(null, 0); } @CollectionFeature.Require(value = {SUPPORTS_ADD, ALLOWS_NULL_VALUES}, absent = RESTRICTS_ELEMENTS) public void testSetCount_addNull_nullSupported() { assertSetCount(null, 1); } @CollectionFeature.Require(value = SUPPORTS_ADD, absent = ALLOWS_NULL_VALUES) public void testSetCount_addNull_nullUnsupported() { try { setCountNoCheckReturnValue(null, 1); fail("adding null with setCount() should throw NullPointerException"); } catch (NullPointerException expected) { } } @CollectionFeature.Require(ALLOWS_NULL_VALUES) public void testSetCount_noOpNull_nullSupported() { try { assertSetCount(null, 0); } catch (UnsupportedOperationException tolerated) { } } @CollectionFeature.Require(absent = ALLOWS_NULL_VALUES) public void testSetCount_noOpNull_nullUnsupported() { try { assertSetCount(null, 0); } catch (NullPointerException tolerated) { } catch (UnsupportedOperationException tolerated) { } } @CollectionSize.Require(absent = ZERO) @CollectionFeature.Require(ALLOWS_NULL_VALUES) public void testSetCount_existingNoNopNull_nullSupported() { initCollectionWithNullElement(); try { assertSetCount(null, 1); } catch (UnsupportedOperationException tolerated) { } } // Negative count. @CollectionFeature.Require(SUPPORTS_REMOVE) public void testSetCount_negative_removeSupported() { try { setCountNoCheckReturnValue(samples.e3, -1); fail("calling setCount() with a negative count should throw " + "IllegalArgumentException"); } catch (IllegalArgumentException expected) { } } @CollectionFeature.Require(absent = SUPPORTS_REMOVE) public void testSetCount_negative_removeUnsupported() { try { setCountNoCheckReturnValue(samples.e3, -1); fail("calling setCount() with a negative count should throw " + "IllegalArgumentException or UnsupportedOperationException"); } catch (IllegalArgumentException expected) { } catch (UnsupportedOperationException expected) { } } // TODO: test adding element of wrong type }