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