AbstractMultisetSetCountTester.java revision 0888a09821a98ac0680fad765217302858e70fa4
1/*
2 * Copyright (C) 2009 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.common.collect.testing.google;
18
19import static com.google.common.collect.testing.features.CollectionFeature.ALLOWS_NULL_VALUES;
20import static com.google.common.collect.testing.features.CollectionFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION;
21import static com.google.common.collect.testing.features.CollectionFeature.RESTRICTS_ELEMENTS;
22import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_ADD;
23import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_REMOVE;
24import static com.google.common.collect.testing.features.CollectionSize.SEVERAL;
25import static com.google.common.collect.testing.features.CollectionSize.ZERO;
26
27import com.google.common.annotations.GwtCompatible;
28import com.google.common.annotations.GwtIncompatible;
29import com.google.common.collect.Multiset;
30import com.google.common.collect.Multiset.Entry;
31import com.google.common.collect.testing.Helpers;
32import com.google.common.collect.testing.features.CollectionFeature;
33import com.google.common.collect.testing.features.CollectionSize;
34
35import java.lang.reflect.Method;
36import java.util.Arrays;
37import java.util.ConcurrentModificationException;
38import java.util.Iterator;
39import java.util.List;
40
41/**
42 * Common superclass for {@link MultisetSetCountUnconditionallyTester} and
43 * {@link MultisetSetCountConditionallyTester}. It is used by those testers to
44 * test calls to the unconditional {@code setCount()} method and calls to the
45 * conditional {@code setCount()} method when the expected present count is
46 * correct.
47 *
48 * @author Chris Povirk
49 */
50@GwtCompatible(emulated = true)
51public abstract class AbstractMultisetSetCountTester<E>
52    extends AbstractMultisetTester<E> {
53  /*
54   * TODO: consider adding MultisetFeatures.SUPPORTS_SET_COUNT. Currently we
55   * assume that using setCount() to increase the count is permitted iff add()
56   * is permitted and similarly for decrease/remove(). We assume that a
57   * setCount() no-op is permitted if either add() or remove() is permitted,
58   * though we also allow it to "succeed" if neither is permitted.
59   */
60
61  private void assertSetCount(E element, int count) {
62    setCountCheckReturnValue(element, count);
63
64    assertEquals(
65        "multiset.count() should return the value passed to setCount()",
66        count, getMultiset().count(element));
67
68    int size = 0;
69    for (Multiset.Entry<E> entry : getMultiset().entrySet()) {
70      size += entry.getCount();
71    }
72    assertEquals(
73        "multiset.size() should be the sum of the counts of all entries",
74        size, getMultiset().size());
75  }
76
77  /**
78   * Call the {@code setCount()} method under test, and check its return value.
79   */
80  abstract void setCountCheckReturnValue(E element, int count);
81
82  /**
83   * Call the {@code setCount()} method under test, but do not check its return
84   * value. Callers should use this method over
85   * {@link #setCountCheckReturnValue(Object, int)} when they expect
86   * {@code setCount()} to throw an exception, as checking the return value
87   * could produce an incorrect error message like
88   * "setCount() should return the original count" instead of the message passed
89   * to a later invocation of {@code fail()}, like "setCount should throw
90   * UnsupportedOperationException."
91   */
92  abstract void setCountNoCheckReturnValue(E element, int count);
93
94  private void assertSetCountIncreasingFailure(E element, int count) {
95    try {
96      setCountNoCheckReturnValue(element, count);
97      fail("a call to multiset.setCount() to increase an element's count "
98          + "should throw");
99    } catch (UnsupportedOperationException expected) {
100    }
101  }
102
103  private void assertSetCountDecreasingFailure(E element, int count) {
104    try {
105      setCountNoCheckReturnValue(element, count);
106      fail("a call to multiset.setCount() to decrease an element's count "
107          + "should throw");
108    } catch (UnsupportedOperationException expected) {
109    }
110  }
111
112  // Unconditional setCount no-ops.
113
114  private void assertZeroToZero() {
115    assertSetCount(samples.e3, 0);
116  }
117
118  private void assertOneToOne() {
119    assertSetCount(samples.e0, 1);
120  }
121
122  private void assertThreeToThree() {
123    initThreeCopies();
124    assertSetCount(samples.e0, 3);
125  }
126
127  @CollectionFeature.Require(SUPPORTS_ADD)
128  public void testSetCount_zeroToZero_addSupported() {
129    assertZeroToZero();
130  }
131
132  @CollectionFeature.Require(SUPPORTS_REMOVE)
133  public void testSetCount_zeroToZero_removeSupported() {
134    assertZeroToZero();
135  }
136
137  @CollectionFeature.Require(absent = {SUPPORTS_ADD, SUPPORTS_REMOVE})
138  public void testSetCount_zeroToZero_unsupported() {
139    try {
140      assertZeroToZero();
141    } catch (UnsupportedOperationException tolerated) {
142    }
143  }
144
145  @CollectionSize.Require(absent = ZERO)
146  @CollectionFeature.Require(SUPPORTS_ADD)
147  public void testSetCount_oneToOne_addSupported() {
148    assertOneToOne();
149  }
150
151  @CollectionSize.Require(absent = ZERO)
152  @CollectionFeature.Require(SUPPORTS_REMOVE)
153  public void testSetCount_oneToOne_removeSupported() {
154    assertOneToOne();
155  }
156
157  @CollectionSize.Require(absent = ZERO)
158  @CollectionFeature.Require(absent = {SUPPORTS_ADD, SUPPORTS_REMOVE})
159  public void testSetCount_oneToOne_unsupported() {
160    try {
161      assertOneToOne();
162    } catch (UnsupportedOperationException tolerated) {
163    }
164  }
165
166  @CollectionSize.Require(SEVERAL)
167  @CollectionFeature.Require(SUPPORTS_ADD)
168  public void testSetCount_threeToThree_addSupported() {
169    assertThreeToThree();
170  }
171
172  @CollectionSize.Require(SEVERAL)
173  @CollectionFeature.Require(SUPPORTS_REMOVE)
174  public void testSetCount_threeToThree_removeSupported() {
175    assertThreeToThree();
176  }
177
178  @CollectionSize.Require(SEVERAL)
179  @CollectionFeature.Require(absent = {SUPPORTS_ADD, SUPPORTS_REMOVE})
180  public void testSetCount_threeToThree_unsupported() {
181    try {
182      assertThreeToThree();
183    } catch (UnsupportedOperationException tolerated) {
184    }
185  }
186
187  // Unconditional setCount size increases:
188
189  @CollectionFeature.Require(SUPPORTS_ADD)
190  public void testSetCount_zeroToOne_supported() {
191    assertSetCount(samples.e3, 1);
192  }
193
194  @CollectionFeature.Require({SUPPORTS_ADD,
195      FAILS_FAST_ON_CONCURRENT_MODIFICATION})
196  public void testSetCountZeroToOneConcurrentWithIteration() {
197    try {
198      Iterator<E> iterator = collection.iterator();
199      assertSetCount(samples.e3, 1);
200      iterator.next();
201      fail("Expected ConcurrentModificationException");
202    } catch (ConcurrentModificationException expected) {
203      // success
204    }
205  }
206
207  @CollectionFeature.Require({SUPPORTS_ADD,
208      FAILS_FAST_ON_CONCURRENT_MODIFICATION})
209  public void testSetCountZeroToOneConcurrentWithEntrySetIteration() {
210    try {
211      Iterator<Entry<E>> iterator = getMultiset().entrySet().iterator();
212      assertSetCount(samples.e3, 1);
213      iterator.next();
214      fail("Expected ConcurrentModificationException");
215    } catch (ConcurrentModificationException expected) {
216      // success
217    }
218  }
219
220  @CollectionFeature.Require(SUPPORTS_ADD)
221  public void testSetCount_zeroToThree_supported() {
222    assertSetCount(samples.e3, 3);
223  }
224
225  @CollectionSize.Require(absent = ZERO)
226  @CollectionFeature.Require(SUPPORTS_ADD)
227  public void testSetCount_oneToThree_supported() {
228    assertSetCount(samples.e0, 3);
229  }
230
231  @CollectionFeature.Require(absent = SUPPORTS_ADD)
232  public void testSetCount_zeroToOne_unsupported() {
233    assertSetCountIncreasingFailure(samples.e3, 1);
234  }
235
236  @CollectionFeature.Require(absent = SUPPORTS_ADD)
237  public void testSetCount_zeroToThree_unsupported() {
238    assertSetCountIncreasingFailure(samples.e3, 3);
239  }
240
241  @CollectionSize.Require(absent = ZERO)
242  @CollectionFeature.Require(absent = SUPPORTS_ADD)
243  public void testSetCount_oneToThree_unsupported() {
244    assertSetCountIncreasingFailure(samples.e3, 3);
245  }
246
247  // Unconditional setCount size decreases:
248
249  @CollectionSize.Require(absent = ZERO)
250  @CollectionFeature.Require(SUPPORTS_REMOVE)
251  public void testSetCount_oneToZero_supported() {
252    assertSetCount(samples.e0, 0);
253  }
254
255  @CollectionFeature.Require({SUPPORTS_REMOVE,
256      FAILS_FAST_ON_CONCURRENT_MODIFICATION})
257  @CollectionSize.Require(absent = ZERO)
258  public void testSetCountOneToZeroConcurrentWithIteration() {
259    try {
260      Iterator<E> iterator = collection.iterator();
261      assertSetCount(samples.e0, 0);
262      iterator.next();
263      fail("Expected ConcurrentModificationException");
264    } catch (ConcurrentModificationException expected) {
265      // success
266    }
267  }
268
269  @CollectionFeature.Require({SUPPORTS_REMOVE,
270      FAILS_FAST_ON_CONCURRENT_MODIFICATION})
271  @CollectionSize.Require(absent = ZERO)
272  public void testSetCountOneToZeroConcurrentWithEntrySetIteration() {
273    try {
274      Iterator<Entry<E>> iterator = getMultiset().entrySet().iterator();
275      assertSetCount(samples.e0, 0);
276      iterator.next();
277      fail("Expected ConcurrentModificationException");
278    } catch (ConcurrentModificationException expected) {
279      // success
280    }
281  }
282
283  @CollectionSize.Require(SEVERAL)
284  @CollectionFeature.Require(SUPPORTS_REMOVE)
285  public void testSetCount_threeToZero_supported() {
286    initThreeCopies();
287    assertSetCount(samples.e0, 0);
288  }
289
290  @CollectionSize.Require(SEVERAL)
291  @CollectionFeature.Require(SUPPORTS_REMOVE)
292  public void testSetCount_threeToOne_supported() {
293    initThreeCopies();
294    assertSetCount(samples.e0, 1);
295  }
296
297  @CollectionSize.Require(absent = ZERO)
298  @CollectionFeature.Require(absent = SUPPORTS_REMOVE)
299  public void testSetCount_oneToZero_unsupported() {
300    assertSetCountDecreasingFailure(samples.e0, 0);
301  }
302
303  @CollectionSize.Require(SEVERAL)
304  @CollectionFeature.Require(absent = SUPPORTS_REMOVE)
305  public void testSetCount_threeToZero_unsupported() {
306    initThreeCopies();
307    assertSetCountDecreasingFailure(samples.e0, 0);
308  }
309
310  @CollectionSize.Require(SEVERAL)
311  @CollectionFeature.Require(absent = SUPPORTS_REMOVE)
312  public void testSetCount_threeToOne_unsupported() {
313    initThreeCopies();
314    assertSetCountDecreasingFailure(samples.e0, 1);
315  }
316
317  // setCount with nulls:
318
319  @CollectionSize.Require(absent = ZERO)
320  @CollectionFeature.Require({SUPPORTS_REMOVE, ALLOWS_NULL_VALUES})
321  public void testSetCount_removeNull_nullSupported() {
322    initCollectionWithNullElement();
323    assertSetCount(null, 0);
324  }
325
326  @CollectionFeature.Require(value = {SUPPORTS_ADD, ALLOWS_NULL_VALUES},
327      absent = RESTRICTS_ELEMENTS)
328  public void testSetCount_addNull_nullSupported() {
329    assertSetCount(null, 1);
330  }
331
332  @CollectionFeature.Require(value = SUPPORTS_ADD, absent = ALLOWS_NULL_VALUES)
333  public void testSetCount_addNull_nullUnsupported() {
334    try {
335      setCountNoCheckReturnValue(null, 1);
336      fail("adding null with setCount() should throw NullPointerException");
337    } catch (NullPointerException expected) {
338    }
339  }
340
341  @CollectionFeature.Require(ALLOWS_NULL_VALUES)
342  public void testSetCount_noOpNull_nullSupported() {
343    try {
344      assertSetCount(null, 0);
345    } catch (UnsupportedOperationException tolerated) {
346    }
347  }
348
349  @CollectionFeature.Require(absent = ALLOWS_NULL_VALUES)
350  public void testSetCount_noOpNull_nullUnsupported() {
351    try {
352      assertSetCount(null, 0);
353    } catch (NullPointerException tolerated) {
354    } catch (UnsupportedOperationException tolerated) {
355    }
356  }
357
358  @CollectionSize.Require(absent = ZERO)
359  @CollectionFeature.Require(ALLOWS_NULL_VALUES)
360  public void testSetCount_existingNoNopNull_nullSupported() {
361    initCollectionWithNullElement();
362    try {
363      assertSetCount(null, 1);
364    } catch (UnsupportedOperationException tolerated) {
365    }
366  }
367
368  // Negative count.
369
370  @CollectionFeature.Require(SUPPORTS_REMOVE)
371  public void testSetCount_negative_removeSupported() {
372    try {
373      setCountNoCheckReturnValue(samples.e3, -1);
374      fail("calling setCount() with a negative count should throw "
375          + "IllegalArgumentException");
376    } catch (IllegalArgumentException expected) {
377    }
378  }
379
380  @CollectionFeature.Require(absent = SUPPORTS_REMOVE)
381  public void testSetCount_negative_removeUnsupported() {
382    try {
383      setCountNoCheckReturnValue(samples.e3, -1);
384      fail("calling setCount() with a negative count should throw "
385          + "IllegalArgumentException or UnsupportedOperationException");
386    } catch (IllegalArgumentException expected) {
387    } catch (UnsupportedOperationException expected) {
388    }
389  }
390
391  // TODO: test adding element of wrong type
392
393  /**
394   * Returns {@link Method} instances for the {@code setCount()} tests that
395   * assume multisets support duplicates so that the test of {@code
396   * Multisets.forSet()} can suppress them.
397   */
398  @GwtIncompatible("reflection")
399  public static List<Method> getSetCountDuplicateInitializingMethods() {
400    return Arrays.asList(
401        getMethod("testSetCount_threeToThree_removeSupported"),
402        getMethod("testSetCount_threeToZero_supported"),
403        getMethod("testSetCount_threeToOne_supported"));
404  }
405
406  @GwtIncompatible("reflection")
407  private static Method getMethod(String methodName) {
408    return Helpers.getMethod(AbstractMultisetSetCountTester.class, methodName);
409  }
410}
411