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