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