001/* 002 * Copyright (C) 2007 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.testers; 018 019import static com.google.common.collect.testing.Helpers.getMethod; 020import static com.google.common.collect.testing.features.CollectionSize.ZERO; 021import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEYS; 022import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES; 023import static com.google.common.collect.testing.features.MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION; 024import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT; 025import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows; 026 027import com.google.common.annotations.GwtCompatible; 028import com.google.common.annotations.GwtIncompatible; 029import com.google.common.annotations.J2ktIncompatible; 030import com.google.common.collect.testing.AbstractMapTester; 031import com.google.common.collect.testing.features.CollectionSize; 032import com.google.common.collect.testing.features.MapFeature; 033import com.google.errorprone.annotations.CanIgnoreReturnValue; 034import java.lang.reflect.Method; 035import java.util.ConcurrentModificationException; 036import java.util.Iterator; 037import java.util.Map.Entry; 038import org.junit.Ignore; 039 040/** 041 * A generic JUnit test which tests {@code put} operations on a map. Can't be invoked directly; 042 * please see {@link com.google.common.collect.testing.MapTestSuiteBuilder}. 043 * 044 * @author Chris Povirk 045 * @author Kevin Bourrillion 046 */ 047@GwtCompatible(emulated = true) 048@Ignore("test runners must not instantiate and run this directly, only via suites we build") 049// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests. 050@SuppressWarnings("JUnit4ClassUsedInJUnit3") 051public class MapPutTester<K, V> extends AbstractMapTester<K, V> { 052 private Entry<K, V> nullKeyEntry; 053 private Entry<K, V> nullValueEntry; 054 private Entry<K, V> nullKeyValueEntry; 055 private Entry<K, V> presentKeyNullValueEntry; 056 057 @Override 058 public void setUp() throws Exception { 059 super.setUp(); 060 nullKeyEntry = entry(null, v3()); 061 nullValueEntry = entry(k3(), null); 062 nullKeyValueEntry = entry(null, null); 063 presentKeyNullValueEntry = entry(k0(), null); 064 } 065 066 @MapFeature.Require(SUPPORTS_PUT) 067 @CollectionSize.Require(absent = ZERO) 068 public void testPut_supportedPresent() { 069 assertEquals("put(present, value) should return the old value", v0(), getMap().put(k0(), v3())); 070 expectReplacement(entry(k0(), v3())); 071 } 072 073 @MapFeature.Require(SUPPORTS_PUT) 074 public void testPut_supportedNotPresent() { 075 assertNull("put(notPresent, value) should return null", put(e3())); 076 expectAdded(e3()); 077 } 078 079 @MapFeature.Require({FAILS_FAST_ON_CONCURRENT_MODIFICATION, SUPPORTS_PUT}) 080 @CollectionSize.Require(absent = ZERO) 081 public void testPutAbsentConcurrentWithEntrySetIteration() { 082 assertThrows( 083 ConcurrentModificationException.class, 084 () -> { 085 Iterator<Entry<K, V>> iterator = getMap().entrySet().iterator(); 086 put(e3()); 087 iterator.next(); 088 }); 089 } 090 091 @MapFeature.Require({FAILS_FAST_ON_CONCURRENT_MODIFICATION, SUPPORTS_PUT}) 092 @CollectionSize.Require(absent = ZERO) 093 public void testPutAbsentConcurrentWithKeySetIteration() { 094 assertThrows( 095 ConcurrentModificationException.class, 096 () -> { 097 Iterator<K> iterator = getMap().keySet().iterator(); 098 put(e3()); 099 iterator.next(); 100 }); 101 } 102 103 @MapFeature.Require({FAILS_FAST_ON_CONCURRENT_MODIFICATION, SUPPORTS_PUT}) 104 @CollectionSize.Require(absent = ZERO) 105 public void testPutAbsentConcurrentWithValueIteration() { 106 assertThrows( 107 ConcurrentModificationException.class, 108 () -> { 109 Iterator<V> iterator = getMap().values().iterator(); 110 put(e3()); 111 iterator.next(); 112 }); 113 } 114 115 @MapFeature.Require(absent = SUPPORTS_PUT) 116 public void testPut_unsupportedNotPresent() { 117 assertThrows(UnsupportedOperationException.class, () -> put(e3())); 118 expectUnchanged(); 119 expectMissing(e3()); 120 } 121 122 @MapFeature.Require(absent = SUPPORTS_PUT) 123 @CollectionSize.Require(absent = ZERO) 124 public void testPut_unsupportedPresentExistingValue() { 125 try { 126 assertEquals("put(present, existingValue) should return present or throw", v0(), put(e0())); 127 } catch (UnsupportedOperationException tolerated) { 128 } 129 expectUnchanged(); 130 } 131 132 @MapFeature.Require(absent = SUPPORTS_PUT) 133 @CollectionSize.Require(absent = ZERO) 134 public void testPut_unsupportedPresentDifferentValue() { 135 assertThrows(UnsupportedOperationException.class, () -> getMap().put(k0(), v3())); 136 expectUnchanged(); 137 } 138 139 @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_KEYS}) 140 public void testPut_nullKeySupportedNotPresent() { 141 assertNull("put(null, value) should return null", put(nullKeyEntry)); 142 expectAdded(nullKeyEntry); 143 } 144 145 @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_KEYS}) 146 @CollectionSize.Require(absent = ZERO) 147 public void testPut_nullKeySupportedPresent() { 148 Entry<K, V> newEntry = entry(null, v3()); 149 initMapWithNullKey(); 150 assertEquals( 151 "put(present, value) should return the associated value", 152 getValueForNullKey(), 153 put(newEntry)); 154 155 Entry<K, V>[] expected = createArrayWithNullKey(); 156 expected[getNullLocation()] = newEntry; 157 expectContents(expected); 158 } 159 160 @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_KEYS) 161 public void testPut_nullKeyUnsupported() { 162 assertThrows(NullPointerException.class, () -> put(nullKeyEntry)); 163 expectUnchanged(); 164 expectNullKeyMissingWhenNullKeysUnsupported( 165 "Should not contain null key after unsupported put(null, value)"); 166 } 167 168 @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_VALUES}) 169 public void testPut_nullValueSupported() { 170 assertNull("put(key, null) should return null", put(nullValueEntry)); 171 expectAdded(nullValueEntry); 172 } 173 174 @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_VALUES) 175 public void testPut_nullValueUnsupported() { 176 assertThrows(NullPointerException.class, () -> put(nullValueEntry)); 177 expectUnchanged(); 178 expectNullValueMissingWhenNullValuesUnsupported( 179 "Should not contain null value after unsupported put(key, null)"); 180 } 181 182 @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_VALUES}) 183 @CollectionSize.Require(absent = ZERO) 184 public void testPut_replaceWithNullValueSupported() { 185 assertEquals( 186 "put(present, null) should return the associated value", 187 v0(), 188 put(presentKeyNullValueEntry)); 189 expectReplacement(presentKeyNullValueEntry); 190 } 191 192 @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_VALUES) 193 @CollectionSize.Require(absent = ZERO) 194 public void testPut_replaceWithNullValueUnsupported() { 195 assertThrows(NullPointerException.class, () -> put(presentKeyNullValueEntry)); 196 expectUnchanged(); 197 expectNullValueMissingWhenNullValuesUnsupported( 198 "Should not contain null after unsupported put(present, null)"); 199 } 200 201 @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_VALUES}) 202 @CollectionSize.Require(absent = ZERO) 203 public void testPut_replaceNullValueWithNullSupported() { 204 initMapWithNullValue(); 205 assertNull( 206 "put(present, null) should return the associated value (null)", 207 getMap().put(getKeyForNullValue(), null)); 208 expectContents(createArrayWithNullValue()); 209 } 210 211 @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_VALUES}) 212 @CollectionSize.Require(absent = ZERO) 213 public void testPut_replaceNullValueWithNonNullSupported() { 214 Entry<K, V> newEntry = entry(getKeyForNullValue(), v3()); 215 initMapWithNullValue(); 216 assertNull("put(present, value) should return the associated value (null)", put(newEntry)); 217 218 Entry<K, V>[] expected = createArrayWithNullValue(); 219 expected[getNullLocation()] = newEntry; 220 expectContents(expected); 221 } 222 223 @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_KEYS, ALLOWS_NULL_VALUES}) 224 public void testPut_nullKeyAndValueSupported() { 225 assertNull("put(null, null) should return null", put(nullKeyValueEntry)); 226 expectAdded(nullKeyValueEntry); 227 } 228 229 @CanIgnoreReturnValue 230 private V put(Entry<K, V> entry) { 231 return getMap().put(entry.getKey(), entry.getValue()); 232 } 233 234 /** 235 * Returns the {@link Method} instance for {@link #testPut_nullKeyUnsupported()} so that tests of 236 * {@link java.util.TreeMap} can suppress it with {@code 237 * FeatureSpecificTestSuiteBuilder.suppressing()} until <a 238 * href="https://bugs.openjdk.org/browse/JDK-5045147">JDK-5045147</a> is fixed. 239 */ 240 @J2ktIncompatible 241 @GwtIncompatible // reflection 242 public static Method getPutNullKeyUnsupportedMethod() { 243 return getMethod(MapPutTester.class, "testPut_nullKeyUnsupported"); 244 } 245}