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 com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.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;
038import org.junit.Ignore;
039
040/**
041 * A generic JUnit test which tests {@link Map#merge}. Can't be invoked directly; please see {@link
042 * com.google.common.collect.testing.MapTestSuiteBuilder}.
043 *
044 * @author Louis Wasserman
045 */
046@GwtCompatible(emulated = true)
047@Ignore("test runners must not instantiate and run this directly, only via suites we build")
048// @Ignore affects the Android test runner, which respects JUnit 4 annotations on JUnit 3 tests.
049@SuppressWarnings("JUnit4ClassUsedInJUnit3")
050public class MapMergeTester<K, V> extends AbstractMapTester<K, V> {
051  @MapFeature.Require(SUPPORTS_PUT)
052  public void testAbsent() {
053    assertEquals(
054        "Map.merge(absent, value, function) should return value",
055        v3(),
056        getMap()
057            .merge(
058                k3(),
059                v3(),
060                (oldV, newV) -> {
061                  throw new AssertionFailedError(
062                      "Should not call merge function if key was absent");
063                }));
064    expectAdded(e3());
065  }
066
067  @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_VALUES})
068  @CollectionSize.Require(absent = ZERO)
069  public void testMappedToNull() {
070    initMapWithNullValue();
071    assertEquals(
072        "Map.merge(keyMappedToNull, value, function) should return value",
073        v3(),
074        getMap()
075            .merge(
076                getKeyForNullValue(),
077                v3(),
078                (oldV, newV) -> {
079                  throw new AssertionFailedError(
080                      "Should not call merge function if key was mapped to null");
081                }));
082    expectReplacement(entry(getKeyForNullValue(), v3()));
083  }
084
085  @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_KEYS})
086  public void testMergeAbsentNullKey() {
087    assertEquals(
088        "Map.merge(null, value, function) should return value",
089        v3(),
090        getMap()
091            .merge(
092                null,
093                v3(),
094                (oldV, newV) -> {
095                  throw new AssertionFailedError(
096                      "Should not call merge function if key was absent");
097                }));
098    expectAdded(entry(null, v3()));
099  }
100
101  @MapFeature.Require(SUPPORTS_PUT)
102  @CollectionSize.Require(absent = ZERO)
103  public void testMergePresent() {
104    assertEquals(
105        "Map.merge(present, value, function) should return function result",
106        v4(),
107        getMap()
108            .merge(
109                k0(),
110                v3(),
111                (oldV, newV) -> {
112                  assertEquals(v0(), oldV);
113                  assertEquals(v3(), newV);
114                  return v4();
115                }));
116    expectReplacement(entry(k0(), v4()));
117  }
118
119  @MapFeature.Require(SUPPORTS_PUT)
120  @CollectionSize.Require(absent = ZERO)
121  public void testMergeFunctionThrows() {
122    assertThrows(
123        SomeUncheckedException.class,
124        () ->
125            getMap()
126                .merge(
127                    k0(),
128                    v3(),
129                    (oldV, newV) -> {
130                      assertEquals(v0(), oldV);
131                      assertEquals(v3(), newV);
132                      throw new SomeUncheckedException();
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    assertThrows(
179        UnsupportedOperationException.class,
180        () ->
181            getMap()
182                .merge(
183                    k3(),
184                    v3(),
185                    (oldV, newV) -> {
186                      throw new AssertionFailedError();
187                    }));
188  }
189
190  /**
191   * Returns the {@link Method} instance for {@link #testMergeNullValue()} so that tests of {@link
192   * Hashtable} can suppress it with {@code FeatureSpecificTestSuiteBuilder.suppressing()}.
193   */
194  @J2ktIncompatible
195  @GwtIncompatible // reflection
196  public static Method getMergeNullValueMethod() {
197    return getMethod(MapMergeTester.class, "testMergeNullValue");
198  }
199}