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