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