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