1 /*
2  * Copyright (C) 2016 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.common.collect.testing.testers;
18 
19 import static com.google.common.collect.testing.features.CollectionSize.ZERO;
20 import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEYS;
21 import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES;
22 import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT;
23 import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_REMOVE;
24 
25 import com.google.common.annotations.GwtCompatible;
26 import com.google.common.annotations.GwtIncompatible;
27 import com.google.common.collect.testing.AbstractMapTester;
28 import com.google.common.collect.testing.Helpers;
29 import com.google.common.collect.testing.features.CollectionSize;
30 import com.google.common.collect.testing.features.MapFeature;
31 import java.lang.reflect.Method;
32 import java.util.Map;
33 import junit.framework.AssertionFailedError;
34 import org.junit.Ignore;
35 
36 /**
37  * A generic JUnit test which tests {@link Map#merge}. Can't be invoked directly; please see {@link
38  * com.google.common.collect.testing.MapTestSuiteBuilder}.
39  *
40  * @author Louis Wasserman
41  */
42 @GwtCompatible(emulated = true)
43 @Ignore // Affects only Android test runner, which respects JUnit 4 annotations on JUnit 3 tests.
44 public class MapMergeTester<K, V> extends AbstractMapTester<K, V> {
45   @MapFeature.Require(SUPPORTS_PUT)
testAbsent()46   public void testAbsent() {
47     assertEquals(
48         "Map.merge(absent, value, function) should return value",
49         v3(),
50         getMap()
51             .merge(
52                 k3(),
53                 v3(),
54                 (oldV, newV) -> {
55                   throw new AssertionFailedError(
56                       "Should not call merge function if key was absent");
57                 }));
58     expectAdded(e3());
59   }
60 
61   @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_VALUES})
62   @CollectionSize.Require(absent = ZERO)
testMappedToNull()63   public void testMappedToNull() {
64     initMapWithNullValue();
65     assertEquals(
66         "Map.merge(keyMappedToNull, value, function) should return value",
67         v3(),
68         getMap()
69             .merge(
70                 getKeyForNullValue(),
71                 v3(),
72                 (oldV, newV) -> {
73                   throw new AssertionFailedError(
74                       "Should not call merge function if key was mapped to null");
75                 }));
76     expectReplacement(entry(getKeyForNullValue(), v3()));
77   }
78 
79   @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_KEYS})
testMergeAbsentNullKey()80   public void testMergeAbsentNullKey() {
81     assertEquals(
82         "Map.merge(null, value, function) should return value",
83         v3(),
84         getMap()
85             .merge(
86                 null,
87                 v3(),
88                 (oldV, newV) -> {
89                   throw new AssertionFailedError(
90                       "Should not call merge function if key was absent");
91                 }));
92     expectAdded(entry(null, v3()));
93   }
94 
95   @MapFeature.Require(SUPPORTS_PUT)
96   @CollectionSize.Require(absent = ZERO)
testMergePresent()97   public void testMergePresent() {
98     assertEquals(
99         "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)
testMergeFunctionThrows()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)
testMergePresentToNull()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 
testMergeNullValue()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 
testMergeNullFunction()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)
testMergeUnsupported()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
getMergeNullValueMethod()193   public static Method getMergeNullValueMethod() {
194     return Helpers.getMethod(MapMergeTester.class, "testMergeNullValue");
195   }
196 }
197