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