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;
024import static com.google.common.collect.testing.testers.ReflectionFreeAssertThrows.assertThrows;
025
026import com.google.common.annotations.GwtCompatible;
027import com.google.common.annotations.GwtIncompatible;
028import com.google.common.annotations.J2ktIncompatible;
029import com.google.common.collect.testing.AbstractMapTester;
030import com.google.common.collect.testing.Helpers;
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")
050@IgnoreJRERequirement // We opt into library desugaring for our tests.
051public class MapMergeTester<K, V> extends AbstractMapTester<K, V> {
052  @MapFeature.Require(SUPPORTS_PUT)
053  public void testAbsent() {
054    assertEquals(
055        "Map.merge(absent, value, function) should return value",
056        v3(),
057        getMap()
058            .merge(
059                k3(),
060                v3(),
061                (oldV, newV) -> {
062                  throw new AssertionFailedError(
063                      "Should not call merge function if key was absent");
064                }));
065    expectAdded(e3());
066  }
067
068  @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_VALUES})
069  @CollectionSize.Require(absent = ZERO)
070  public void testMappedToNull() {
071    initMapWithNullValue();
072    assertEquals(
073        "Map.merge(keyMappedToNull, value, function) should return value",
074        v3(),
075        getMap()
076            .merge(
077                getKeyForNullValue(),
078                v3(),
079                (oldV, newV) -> {
080                  throw new AssertionFailedError(
081                      "Should not call merge function if key was mapped to null");
082                }));
083    expectReplacement(entry(getKeyForNullValue(), v3()));
084  }
085
086  @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_KEYS})
087  public void testMergeAbsentNullKey() {
088    assertEquals(
089        "Map.merge(null, value, function) should return value",
090        v3(),
091        getMap()
092            .merge(
093                null,
094                v3(),
095                (oldV, newV) -> {
096                  throw new AssertionFailedError(
097                      "Should not call merge function if key was absent");
098                }));
099    expectAdded(entry(null, v3()));
100  }
101
102  @MapFeature.Require(SUPPORTS_PUT)
103  @CollectionSize.Require(absent = ZERO)
104  public void testMergePresent() {
105    assertEquals(
106        "Map.merge(present, value, function) should return function result",
107        v4(),
108        getMap()
109            .merge(
110                k0(),
111                v3(),
112                (oldV, newV) -> {
113                  assertEquals(v0(), oldV);
114                  assertEquals(v3(), newV);
115                  return v4();
116                }));
117    expectReplacement(entry(k0(), v4()));
118  }
119
120  @MapFeature.Require(SUPPORTS_PUT)
121  @CollectionSize.Require(absent = ZERO)
122  public void testMergeFunctionThrows() {
123    assertThrows(
124        SomeUncheckedException.class,
125        () ->
126            getMap()
127                .merge(
128                    k0(),
129                    v3(),
130                    (oldV, newV) -> {
131                      assertEquals(v0(), oldV);
132                      assertEquals(v3(), newV);
133                      throw new SomeUncheckedException();
134                    }));
135    expectUnchanged();
136  }
137
138  @MapFeature.Require(SUPPORTS_REMOVE)
139  @CollectionSize.Require(absent = ZERO)
140  public void testMergePresentToNull() {
141    assertNull(
142        "Map.merge(present, value, functionReturningNull) should return null",
143        getMap()
144            .merge(
145                k0(),
146                v3(),
147                (oldV, newV) -> {
148                  assertEquals(v0(), oldV);
149                  assertEquals(v3(), newV);
150                  return null;
151                }));
152    expectMissing(e0());
153  }
154
155  public void testMergeNullValue() {
156    try {
157      getMap()
158          .merge(
159              k0(),
160              null,
161              (oldV, newV) -> {
162                throw new AssertionFailedError("Should not call merge function if value was null");
163              });
164      fail("Expected NullPointerException or UnsupportedOperationException");
165    } catch (NullPointerException | UnsupportedOperationException expected) {
166    }
167  }
168
169  public void testMergeNullFunction() {
170    try {
171      getMap().merge(k0(), v3(), null);
172      fail("Expected NullPointerException or UnsupportedOperationException");
173    } catch (NullPointerException | UnsupportedOperationException expected) {
174    }
175  }
176
177  @MapFeature.Require(absent = SUPPORTS_PUT)
178  public void testMergeUnsupported() {
179    assertThrows(
180        UnsupportedOperationException.class,
181        () ->
182            getMap()
183                .merge(
184                    k3(),
185                    v3(),
186                    (oldV, newV) -> {
187                      throw new AssertionFailedError();
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}