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