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