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.
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  private static class ExpectedException extends RuntimeException {}
116
117  @MapFeature.Require(SUPPORTS_PUT)
118  @CollectionSize.Require(absent = ZERO)
119  public void testMergeFunctionThrows() {
120    try {
121      getMap()
122          .merge(
123              k0(),
124              v3(),
125              (oldV, newV) -> {
126                assertEquals(v0(), oldV);
127                assertEquals(v3(), newV);
128                throw new ExpectedException();
129              });
130      fail("Expected ExpectedException");
131    } catch (ExpectedException expected) {
132    }
133    expectUnchanged();
134  }
135
136  @MapFeature.Require(SUPPORTS_REMOVE)
137  @CollectionSize.Require(absent = ZERO)
138  public void testMergePresentToNull() {
139    assertNull(
140        "Map.merge(present, value, functionReturningNull) should return null",
141        getMap()
142            .merge(
143                k0(),
144                v3(),
145                (oldV, newV) -> {
146                  assertEquals(v0(), oldV);
147                  assertEquals(v3(), newV);
148                  return null;
149                }));
150    expectMissing(e0());
151  }
152
153  public void testMergeNullValue() {
154    try {
155      getMap()
156          .merge(
157              k0(),
158              null,
159              (oldV, newV) -> {
160                throw new AssertionFailedError("Should not call merge function if value was null");
161              });
162      fail("Expected NullPointerException or UnsupportedOperationException");
163    } catch (NullPointerException | UnsupportedOperationException expected) {
164    }
165  }
166
167  public void testMergeNullFunction() {
168    try {
169      getMap().merge(k0(), v3(), null);
170      fail("Expected NullPointerException or UnsupportedOperationException");
171    } catch (NullPointerException | UnsupportedOperationException expected) {
172    }
173  }
174
175  @MapFeature.Require(absent = SUPPORTS_PUT)
176  public void testMergeUnsupported() {
177    try {
178      getMap()
179          .merge(
180              k3(),
181              v3(),
182              (oldV, newV) -> {
183                throw new AssertionFailedError();
184              });
185      fail("Expected UnsupportedOperationException");
186    } catch (UnsupportedOperationException expected) {
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 Helpers.getMethod(MapMergeTester.class, "testMergeNullValue");
198  }
199}