001/*
002 * Copyright (C) 2009 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.google.common.collect.testing.google;
018
019import static com.google.common.collect.testing.features.CollectionFeature.ALLOWS_NULL_VALUES;
020import static com.google.common.collect.testing.features.CollectionFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION;
021import static com.google.common.collect.testing.features.CollectionFeature.RESTRICTS_ELEMENTS;
022import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_ADD;
023import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_REMOVE;
024import static com.google.common.collect.testing.features.CollectionSize.SEVERAL;
025import static com.google.common.collect.testing.features.CollectionSize.ZERO;
026
027import com.google.common.annotations.GwtCompatible;
028import com.google.common.annotations.GwtIncompatible;
029import com.google.common.annotations.J2ktIncompatible;
030import com.google.common.collect.Multiset;
031import com.google.common.collect.Multiset.Entry;
032import com.google.common.collect.testing.Helpers;
033import com.google.common.collect.testing.features.CollectionFeature;
034import com.google.common.collect.testing.features.CollectionSize;
035import java.lang.reflect.Method;
036import java.util.Arrays;
037import java.util.ConcurrentModificationException;
038import java.util.Iterator;
039import java.util.List;
040import org.junit.Ignore;
041
042/**
043 * Common superclass for {@link MultisetSetCountUnconditionallyTester} and {@link
044 * MultisetSetCountConditionallyTester}. It is used by those testers to test calls to the
045 * unconditional {@code setCount()} method and calls to the conditional {@code setCount()} method
046 * when the expected present count is correct.
047 *
048 * @author Chris Povirk
049 */
050@GwtCompatible(emulated = true)
051@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests.
052public abstract class AbstractMultisetSetCountTester<E> extends AbstractMultisetTester<E> {
053  /*
054   * TODO: consider adding MultisetFeatures.SUPPORTS_SET_COUNT. Currently we
055   * assume that using setCount() to increase the count is permitted iff add()
056   * is permitted and similarly for decrease/remove(). We assume that a
057   * setCount() no-op is permitted if either add() or remove() is permitted,
058   * though we also allow it to "succeed" if neither is permitted.
059   */
060
061  private void assertSetCount(E element, int count) {
062    setCountCheckReturnValue(element, count);
063
064    assertEquals(
065        "multiset.count() should return the value passed to setCount()",
066        count,
067        getMultiset().count(element));
068
069    int size = 0;
070    for (Multiset.Entry<E> entry : getMultiset().entrySet()) {
071      size += entry.getCount();
072    }
073    assertEquals(
074        "multiset.size() should be the sum of the counts of all entries",
075        size,
076        getMultiset().size());
077  }
078
079  /** Call the {@code setCount()} method under test, and check its return value. */
080  abstract void setCountCheckReturnValue(E element, int count);
081
082  /**
083   * Call the {@code setCount()} method under test, but do not check its return value. Callers
084   * should use this method over {@link #setCountCheckReturnValue(Object, int)} when they expect
085   * {@code setCount()} to throw an exception, as checking the return value could produce an
086   * incorrect error message like "setCount() should return the original count" instead of the
087   * message passed to a later invocation of {@code fail()}, like "setCount should throw
088   * UnsupportedOperationException."
089   */
090  abstract void setCountNoCheckReturnValue(E element, int count);
091
092  private void assertSetCountIncreasingFailure(E element, int count) {
093    try {
094      setCountNoCheckReturnValue(element, count);
095      fail("a call to multiset.setCount() to increase an element's count should throw");
096    } catch (UnsupportedOperationException expected) {
097    }
098  }
099
100  private void assertSetCountDecreasingFailure(E element, int count) {
101    try {
102      setCountNoCheckReturnValue(element, count);
103      fail("a call to multiset.setCount() to decrease an element's count should throw");
104    } catch (UnsupportedOperationException expected) {
105    }
106  }
107
108  // Unconditional setCount no-ops.
109
110  private void assertZeroToZero() {
111    assertSetCount(e3(), 0);
112  }
113
114  private void assertOneToOne() {
115    assertSetCount(e0(), 1);
116  }
117
118  private void assertThreeToThree() {
119    initThreeCopies();
120    assertSetCount(e0(), 3);
121  }
122
123  @CollectionFeature.Require(SUPPORTS_ADD)
124  public void testSetCount_zeroToZero_addSupported() {
125    assertZeroToZero();
126  }
127
128  @CollectionFeature.Require(SUPPORTS_REMOVE)
129  public void testSetCount_zeroToZero_removeSupported() {
130    assertZeroToZero();
131  }
132
133  @CollectionFeature.Require(absent = {SUPPORTS_ADD, SUPPORTS_REMOVE})
134  public void testSetCount_zeroToZero_unsupported() {
135    try {
136      assertZeroToZero();
137    } catch (UnsupportedOperationException tolerated) {
138    }
139  }
140
141  @CollectionSize.Require(absent = ZERO)
142  @CollectionFeature.Require(SUPPORTS_ADD)
143  public void testSetCount_oneToOne_addSupported() {
144    assertOneToOne();
145  }
146
147  @CollectionSize.Require(absent = ZERO)
148  @CollectionFeature.Require(SUPPORTS_REMOVE)
149  public void testSetCount_oneToOne_removeSupported() {
150    assertOneToOne();
151  }
152
153  @CollectionSize.Require(absent = ZERO)
154  @CollectionFeature.Require(absent = {SUPPORTS_ADD, SUPPORTS_REMOVE})
155  public void testSetCount_oneToOne_unsupported() {
156    try {
157      assertOneToOne();
158    } catch (UnsupportedOperationException tolerated) {
159    }
160  }
161
162  @CollectionSize.Require(SEVERAL)
163  @CollectionFeature.Require(SUPPORTS_ADD)
164  public void testSetCount_threeToThree_addSupported() {
165    assertThreeToThree();
166  }
167
168  @CollectionSize.Require(SEVERAL)
169  @CollectionFeature.Require(SUPPORTS_REMOVE)
170  public void testSetCount_threeToThree_removeSupported() {
171    assertThreeToThree();
172  }
173
174  @CollectionSize.Require(SEVERAL)
175  @CollectionFeature.Require(absent = {SUPPORTS_ADD, SUPPORTS_REMOVE})
176  public void testSetCount_threeToThree_unsupported() {
177    try {
178      assertThreeToThree();
179    } catch (UnsupportedOperationException tolerated) {
180    }
181  }
182
183  // Unconditional setCount size increases:
184
185  @CollectionFeature.Require(SUPPORTS_ADD)
186  public void testSetCount_zeroToOne_supported() {
187    assertSetCount(e3(), 1);
188  }
189
190  @CollectionFeature.Require({SUPPORTS_ADD, FAILS_FAST_ON_CONCURRENT_MODIFICATION})
191  public void testSetCountZeroToOneConcurrentWithIteration() {
192    Iterator<E> iterator = collection.iterator();
193    assertSetCount(e3(), 1);
194    try {
195      iterator.next();
196      fail("Expected ConcurrentModificationException");
197    } catch (ConcurrentModificationException expected) {
198      // success
199    }
200  }
201
202  @CollectionFeature.Require({SUPPORTS_ADD, FAILS_FAST_ON_CONCURRENT_MODIFICATION})
203  public void testSetCountZeroToOneConcurrentWithEntrySetIteration() {
204    Iterator<Entry<E>> iterator = getMultiset().entrySet().iterator();
205    assertSetCount(e3(), 1);
206    try {
207      iterator.next();
208      fail("Expected ConcurrentModificationException");
209    } catch (ConcurrentModificationException expected) {
210      // success
211    }
212  }
213
214  @CollectionFeature.Require(SUPPORTS_ADD)
215  public void testSetCount_zeroToThree_supported() {
216    assertSetCount(e3(), 3);
217  }
218
219  @CollectionSize.Require(absent = ZERO)
220  @CollectionFeature.Require(SUPPORTS_ADD)
221  public void testSetCount_oneToThree_supported() {
222    assertSetCount(e0(), 3);
223  }
224
225  @CollectionFeature.Require(absent = SUPPORTS_ADD)
226  public void testSetCount_zeroToOne_unsupported() {
227    assertSetCountIncreasingFailure(e3(), 1);
228  }
229
230  @CollectionFeature.Require(absent = SUPPORTS_ADD)
231  public void testSetCount_zeroToThree_unsupported() {
232    assertSetCountIncreasingFailure(e3(), 3);
233  }
234
235  @CollectionSize.Require(absent = ZERO)
236  @CollectionFeature.Require(absent = SUPPORTS_ADD)
237  public void testSetCount_oneToThree_unsupported() {
238    assertSetCountIncreasingFailure(e3(), 3);
239  }
240
241  // Unconditional setCount size decreases:
242
243  @CollectionSize.Require(absent = ZERO)
244  @CollectionFeature.Require(SUPPORTS_REMOVE)
245  public void testSetCount_oneToZero_supported() {
246    assertSetCount(e0(), 0);
247  }
248
249  @CollectionFeature.Require({SUPPORTS_REMOVE, FAILS_FAST_ON_CONCURRENT_MODIFICATION})
250  @CollectionSize.Require(absent = ZERO)
251  public void testSetCountOneToZeroConcurrentWithIteration() {
252    Iterator<E> iterator = collection.iterator();
253    assertSetCount(e0(), 0);
254    try {
255      iterator.next();
256      fail("Expected ConcurrentModificationException");
257    } catch (ConcurrentModificationException expected) {
258      // success
259    }
260  }
261
262  @CollectionFeature.Require({SUPPORTS_REMOVE, FAILS_FAST_ON_CONCURRENT_MODIFICATION})
263  @CollectionSize.Require(absent = ZERO)
264  public void testSetCountOneToZeroConcurrentWithEntrySetIteration() {
265    Iterator<Entry<E>> iterator = getMultiset().entrySet().iterator();
266    assertSetCount(e0(), 0);
267    try {
268      iterator.next();
269      fail("Expected ConcurrentModificationException");
270    } catch (ConcurrentModificationException expected) {
271      // success
272    }
273  }
274
275  @CollectionSize.Require(SEVERAL)
276  @CollectionFeature.Require(SUPPORTS_REMOVE)
277  public void testSetCount_threeToZero_supported() {
278    initThreeCopies();
279    assertSetCount(e0(), 0);
280  }
281
282  @CollectionSize.Require(SEVERAL)
283  @CollectionFeature.Require(SUPPORTS_REMOVE)
284  public void testSetCount_threeToOne_supported() {
285    initThreeCopies();
286    assertSetCount(e0(), 1);
287  }
288
289  @CollectionSize.Require(absent = ZERO)
290  @CollectionFeature.Require(absent = SUPPORTS_REMOVE)
291  public void testSetCount_oneToZero_unsupported() {
292    assertSetCountDecreasingFailure(e0(), 0);
293  }
294
295  @CollectionSize.Require(SEVERAL)
296  @CollectionFeature.Require(absent = SUPPORTS_REMOVE)
297  public void testSetCount_threeToZero_unsupported() {
298    initThreeCopies();
299    assertSetCountDecreasingFailure(e0(), 0);
300  }
301
302  @CollectionSize.Require(SEVERAL)
303  @CollectionFeature.Require(absent = SUPPORTS_REMOVE)
304  public void testSetCount_threeToOne_unsupported() {
305    initThreeCopies();
306    assertSetCountDecreasingFailure(e0(), 1);
307  }
308
309  // setCount with nulls:
310
311  @CollectionSize.Require(absent = ZERO)
312  @CollectionFeature.Require({SUPPORTS_REMOVE, ALLOWS_NULL_VALUES})
313  public void testSetCount_removeNull_nullSupported() {
314    initCollectionWithNullElement();
315    assertSetCount(null, 0);
316  }
317
318  @CollectionFeature.Require(
319      value = {SUPPORTS_ADD, ALLOWS_NULL_VALUES},
320      absent = RESTRICTS_ELEMENTS)
321  public void testSetCount_addNull_nullSupported() {
322    assertSetCount(null, 1);
323  }
324
325  @CollectionFeature.Require(value = SUPPORTS_ADD, absent = ALLOWS_NULL_VALUES)
326  public void testSetCount_addNull_nullUnsupported() {
327    try {
328      setCountNoCheckReturnValue(null, 1);
329      fail("adding null with setCount() should throw NullPointerException");
330    } catch (NullPointerException expected) {
331    }
332  }
333
334  @CollectionFeature.Require(ALLOWS_NULL_VALUES)
335  public void testSetCount_noOpNull_nullSupported() {
336    try {
337      assertSetCount(null, 0);
338    } catch (UnsupportedOperationException tolerated) {
339    }
340  }
341
342  @CollectionFeature.Require(absent = ALLOWS_NULL_VALUES)
343  public void testSetCount_noOpNull_nullUnsupported() {
344    try {
345      assertSetCount(null, 0);
346    } catch (NullPointerException | UnsupportedOperationException tolerated) {
347    }
348  }
349
350  @CollectionSize.Require(absent = ZERO)
351  @CollectionFeature.Require(ALLOWS_NULL_VALUES)
352  public void testSetCount_existingNoNopNull_nullSupported() {
353    initCollectionWithNullElement();
354    try {
355      assertSetCount(null, 1);
356    } catch (UnsupportedOperationException tolerated) {
357    }
358  }
359
360  // Negative count.
361
362  @CollectionFeature.Require(SUPPORTS_REMOVE)
363  public void testSetCount_negative_removeSupported() {
364    try {
365      setCountNoCheckReturnValue(e3(), -1);
366      fail("calling setCount() with a negative count should throw IllegalArgumentException");
367    } catch (IllegalArgumentException expected) {
368    }
369  }
370
371  @CollectionFeature.Require(absent = SUPPORTS_REMOVE)
372  public void testSetCount_negative_removeUnsupported() {
373    try {
374      setCountNoCheckReturnValue(e3(), -1);
375      fail(
376          "calling setCount() with a negative count should throw "
377              + "IllegalArgumentException or UnsupportedOperationException");
378    } catch (IllegalArgumentException | UnsupportedOperationException expected) {
379    }
380  }
381
382  // TODO: test adding element of wrong type
383
384  /**
385   * Returns {@link Method} instances for the {@code setCount()} tests that assume multisets support
386   * duplicates so that the test of {@code Multisets.forSet()} can suppress them.
387   */
388  @J2ktIncompatible
389  @GwtIncompatible // reflection
390  public static List<Method> getSetCountDuplicateInitializingMethods() {
391    return Arrays.asList(
392        getMethod("testSetCount_threeToThree_removeSupported"),
393        getMethod("testSetCount_threeToZero_supported"),
394        getMethod("testSetCount_threeToOne_supported"));
395  }
396
397  @J2ktIncompatible
398  @GwtIncompatible // reflection
399  private static Method getMethod(String methodName) {
400    return Helpers.getMethod(AbstractMultisetSetCountTester.class, methodName);
401  }
402}