001/*
002 * Copyright (C) 2016 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.SUPPORTS_PUT;
023import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_REMOVE;
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 java.lang.reflect.Method;
033import java.util.Hashtable;
034import java.util.Map;
035import junit.framework.AssertionFailedError;
036import org.junit.Ignore;
037
038/**
039 * A generic JUnit test which tests {@link Map#merge}. Can't be invoked directly; please see {@link
040 * com.google.common.collect.testing.MapTestSuiteBuilder}.
041 *
042 * @author Louis Wasserman
043 */
044@GwtCompatible(emulated = true)
045@Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests.
046@SuppressWarnings("JUnit4ClassUsedInJUnit3")
047public class MapMergeTester<K, V> extends AbstractMapTester<K, V> {
048  @MapFeature.Require(SUPPORTS_PUT)
049  public void testAbsent() {
050    assertEquals(
051        "Map.merge(absent, value, function) should return value",
052        v3(),
053        getMap()
054            .merge(
055                k3(),
056                v3(),
057                (oldV, newV) -> {
058                  throw new AssertionFailedError(
059                      "Should not call merge function if key was absent");
060                }));
061    expectAdded(e3());
062  }
063
064  @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_VALUES})
065  @CollectionSize.Require(absent = ZERO)
066  public void testMappedToNull() {
067    initMapWithNullValue();
068    assertEquals(
069        "Map.merge(keyMappedToNull, value, function) should return value",
070        v3(),
071        getMap()
072            .merge(
073                getKeyForNullValue(),
074                v3(),
075                (oldV, newV) -> {
076                  throw new AssertionFailedError(
077                      "Should not call merge function if key was mapped to null");
078                }));
079    expectReplacement(entry(getKeyForNullValue(), v3()));
080  }
081
082  @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_KEYS})
083  public void testMergeAbsentNullKey() {
084    assertEquals(
085        "Map.merge(null, value, function) should return value",
086        v3(),
087        getMap()
088            .merge(
089                null,
090                v3(),
091                (oldV, newV) -> {
092                  throw new AssertionFailedError(
093                      "Should not call merge function if key was absent");
094                }));
095    expectAdded(entry(null, v3()));
096  }
097
098  @MapFeature.Require(SUPPORTS_PUT)
099  @CollectionSize.Require(absent = ZERO)
100  public void testMergePresent() {
101    assertEquals(
102        "Map.merge(present, value, function) should return function result",
103        v4(),
104        getMap()
105            .merge(
106                k0(),
107                v3(),
108                (oldV, newV) -> {
109                  assertEquals(v0(), oldV);
110                  assertEquals(v3(), newV);
111                  return v4();
112                }));
113    expectReplacement(entry(k0(), v4()));
114  }
115
116  private static class ExpectedException extends RuntimeException {}
117
118  @MapFeature.Require(SUPPORTS_PUT)
119  @CollectionSize.Require(absent = ZERO)
120  public void testMergeFunctionThrows() {
121    try {
122      getMap()
123          .merge(
124              k0(),
125              v3(),
126              (oldV, newV) -> {
127                assertEquals(v0(), oldV);
128                assertEquals(v3(), newV);
129                throw new ExpectedException();
130              });
131      fail("Expected ExpectedException");
132    } catch (ExpectedException expected) {
133    }
134    expectUnchanged();
135  }
136
137  @MapFeature.Require(SUPPORTS_REMOVE)
138  @CollectionSize.Require(absent = ZERO)
139  public void testMergePresentToNull() {
140    assertNull(
141        "Map.merge(present, value, functionReturningNull) should return null",
142        getMap()
143            .merge(
144                k0(),
145                v3(),
146                (oldV, newV) -> {
147                  assertEquals(v0(), oldV);
148                  assertEquals(v3(), newV);
149                  return null;
150                }));
151    expectMissing(e0());
152  }
153
154  public void testMergeNullValue() {
155    try {
156      getMap()
157          .merge(
158              k0(),
159              null,
160              (oldV, newV) -> {
161                throw new AssertionFailedError("Should not call merge function if value was null");
162              });
163      fail("Expected NullPointerException or UnsupportedOperationException");
164    } catch (NullPointerException | UnsupportedOperationException expected) {
165    }
166  }
167
168  public void testMergeNullFunction() {
169    try {
170      getMap().merge(k0(), v3(), null);
171      fail("Expected NullPointerException or UnsupportedOperationException");
172    } catch (NullPointerException | UnsupportedOperationException expected) {
173    }
174  }
175
176  @MapFeature.Require(absent = SUPPORTS_PUT)
177  public void testMergeUnsupported() {
178    try {
179      getMap()
180          .merge(
181              k3(),
182              v3(),
183              (oldV, newV) -> {
184                throw new AssertionFailedError();
185              });
186      fail("Expected UnsupportedOperationException");
187    } catch (UnsupportedOperationException expected) {
188    }
189  }
190
191  /**
192   * Returns the {@link Method} instance for {@link #testMergeNullValue()} so that tests of {@link
193   * Hashtable} can suppress it with {@code FeatureSpecificTestSuiteBuilder.suppressing()}.
194   */
195  @J2ktIncompatible
196  @GwtIncompatible // reflection
197  public static Method getMergeNullValueMethod() {
198    return Helpers.getMethod(MapMergeTester.class, "testMergeNullValue");
199  }
200}