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