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