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