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;
024import static java.util.Collections.singletonList;
025
026import com.google.common.annotations.GwtCompatible;
027import com.google.common.annotations.GwtIncompatible;
028import com.google.common.collect.testing.AbstractMapTester;
029import com.google.common.collect.testing.Helpers;
030import com.google.common.collect.testing.MinimalCollection;
031import com.google.common.collect.testing.features.CollectionSize;
032import com.google.common.collect.testing.features.MapFeature;
033import java.lang.reflect.Method;
034import java.util.Collections;
035import java.util.ConcurrentModificationException;
036import java.util.Iterator;
037import java.util.LinkedHashMap;
038import java.util.List;
039import java.util.Map;
040import java.util.Map.Entry;
041import org.junit.Ignore;
042
043/**
044 * A generic JUnit test which tests {@code putAll} operations on a map. Can't be invoked directly;
045 * please see {@link com.google.common.collect.testing.MapTestSuiteBuilder}.
046 *
047 * @author Chris Povirk
048 * @author Kevin Bourrillion
049 */
050@SuppressWarnings("unchecked") // too many "unchecked generic array creations"
051@GwtCompatible(emulated = true)
052@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests.
053public class MapPutAllTester<K, V> extends AbstractMapTester<K, V> {
054  private List<Entry<K, V>> containsNullKey;
055  private List<Entry<K, V>> containsNullValue;
056
057  @Override
058  public void setUp() throws Exception {
059    super.setUp();
060    containsNullKey = singletonList(entry(null, v3()));
061    containsNullValue = singletonList(entry(k3(), null));
062  }
063
064  @MapFeature.Require(SUPPORTS_PUT)
065  public void testPutAll_supportedNothing() {
066    getMap().putAll(emptyMap());
067    expectUnchanged();
068  }
069
070  @MapFeature.Require(absent = SUPPORTS_PUT)
071  public void testPutAll_unsupportedNothing() {
072    try {
073      getMap().putAll(emptyMap());
074    } catch (UnsupportedOperationException tolerated) {
075    }
076    expectUnchanged();
077  }
078
079  @MapFeature.Require(SUPPORTS_PUT)
080  public void testPutAll_supportedNonePresent() {
081    putAll(createDisjointCollection());
082    expectAdded(e3(), e4());
083  }
084
085  @MapFeature.Require(absent = SUPPORTS_PUT)
086  public void testPutAll_unsupportedNonePresent() {
087    try {
088      putAll(createDisjointCollection());
089      fail("putAll(nonePresent) should throw");
090    } catch (UnsupportedOperationException expected) {
091    }
092    expectUnchanged();
093    expectMissing(e3(), e4());
094  }
095
096  @MapFeature.Require(SUPPORTS_PUT)
097  @CollectionSize.Require(absent = ZERO)
098  public void testPutAll_supportedSomePresent() {
099    putAll(MinimalCollection.of(e3(), e0()));
100    expectAdded(e3());
101  }
102
103  @MapFeature.Require({FAILS_FAST_ON_CONCURRENT_MODIFICATION, SUPPORTS_PUT})
104  @CollectionSize.Require(absent = ZERO)
105  public void testPutAllSomePresentConcurrentWithEntrySetIteration() {
106    try {
107      Iterator<Entry<K, V>> iterator = getMap().entrySet().iterator();
108      putAll(MinimalCollection.of(e3(), e0()));
109      iterator.next();
110      fail("Expected ConcurrentModificationException");
111    } catch (ConcurrentModificationException expected) {
112      // success
113    }
114  }
115
116  @MapFeature.Require(absent = SUPPORTS_PUT)
117  @CollectionSize.Require(absent = ZERO)
118  public void testPutAll_unsupportedSomePresent() {
119    try {
120      putAll(MinimalCollection.of(e3(), e0()));
121      fail("putAll(somePresent) should throw");
122    } catch (UnsupportedOperationException expected) {
123    }
124    expectUnchanged();
125  }
126
127  @MapFeature.Require(absent = SUPPORTS_PUT)
128  @CollectionSize.Require(absent = ZERO)
129  public void testPutAll_unsupportedAllPresent() {
130    try {
131      putAll(MinimalCollection.of(e0()));
132    } catch (UnsupportedOperationException tolerated) {
133    }
134    expectUnchanged();
135  }
136
137  @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_KEYS})
138  public void testPutAll_nullKeySupported() {
139    putAll(containsNullKey);
140    expectAdded(containsNullKey.get(0));
141  }
142
143  @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_KEYS)
144  public void testPutAll_nullKeyUnsupported() {
145    try {
146      putAll(containsNullKey);
147      fail("putAll(containsNullKey) should throw");
148    } catch (NullPointerException expected) {
149    }
150    expectUnchanged();
151    expectNullKeyMissingWhenNullKeysUnsupported(
152        "Should not contain null key after unsupported putAll(containsNullKey)");
153  }
154
155  @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_VALUES})
156  public void testPutAll_nullValueSupported() {
157    putAll(containsNullValue);
158    expectAdded(containsNullValue.get(0));
159  }
160
161  @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_VALUES)
162  public void testPutAll_nullValueUnsupported() {
163    try {
164      putAll(containsNullValue);
165      fail("putAll(containsNullValue) should throw");
166    } catch (NullPointerException expected) {
167    }
168    expectUnchanged();
169    expectNullValueMissingWhenNullValuesUnsupported(
170        "Should not contain null value after unsupported putAll(containsNullValue)");
171  }
172
173  @MapFeature.Require(SUPPORTS_PUT)
174  public void testPutAll_nullCollectionReference() {
175    try {
176      getMap().putAll(null);
177      fail("putAll(null) should throw NullPointerException");
178    } catch (NullPointerException expected) {
179    }
180  }
181
182  private Map<K, V> emptyMap() {
183    return Collections.emptyMap();
184  }
185
186  private void putAll(Iterable<Entry<K, V>> entries) {
187    Map<K, V> map = new LinkedHashMap<>();
188    for (Entry<K, V> entry : entries) {
189      map.put(entry.getKey(), entry.getValue());
190    }
191    getMap().putAll(map);
192  }
193
194  /**
195   * Returns the {@link Method} instance for {@link #testPutAll_nullKeyUnsupported()} so that tests
196   * can suppress it with {@code FeatureSpecificTestSuiteBuilder.suppressing()} until <a
197   * href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5045147">Sun bug 5045147</a> is fixed.
198   */
199  @GwtIncompatible // reflection
200  public static Method getPutAllNullKeyUnsupportedMethod() {
201    return Helpers.getMethod(MapPutAllTester.class, "testPutAll_nullKeyUnsupported");
202  }
203}