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