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