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