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