1 /*
2  * Copyright (C) 2008 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;
18 
19 import com.google.common.annotations.GwtCompatible;
20 import com.google.common.base.Function;
21 import com.google.common.base.Functions;
22 import com.google.common.collect.testing.MapInterfaceTest;
23 import java.util.Collection;
24 import java.util.Iterator;
25 import java.util.Map;
26 import java.util.Map.Entry;
27 import java.util.Set;
28 import org.checkerframework.checker.nullness.qual.Nullable;
29 
30 /**
31  * Tests for {@link Maps#transformValues}.
32  *
33  * @author Isaac Shum
34  */
35 @GwtCompatible
36 public class MapsTransformValuesTest extends MapInterfaceTest<String, String> {
37 
38   /**
39    * Constructor that assigns {@code supportsIteratorRemove} the same value as {@code
40    * supportsRemove}.
41    */
MapsTransformValuesTest( boolean allowsNullKeys, boolean allowsNullValues, boolean supportsPut, boolean supportsRemove, boolean supportsClear)42   protected MapsTransformValuesTest(
43       boolean allowsNullKeys,
44       boolean allowsNullValues,
45       boolean supportsPut,
46       boolean supportsRemove,
47       boolean supportsClear) {
48     super(
49         allowsNullKeys,
50         allowsNullValues,
51         supportsPut,
52         supportsRemove,
53         supportsClear,
54         supportsRemove);
55   }
56 
MapsTransformValuesTest()57   public MapsTransformValuesTest() {
58     super(false, true, false, true, true);
59   }
60 
61   @Override
makeEmptyMap()62   protected Map<String, String> makeEmptyMap() {
63     return Maps.transformValues(Maps.<String, String>newHashMap(), Functions.<String>identity());
64   }
65 
66   @Override
makePopulatedMap()67   protected Map<String, String> makePopulatedMap() {
68     Map<String, Integer> underlying = Maps.newHashMap();
69     underlying.put("a", 1);
70     underlying.put("b", 2);
71     underlying.put("c", 3);
72     return Maps.transformValues(underlying, Functions.toStringFunction());
73   }
74 
75   @Override
getKeyNotInPopulatedMap()76   protected String getKeyNotInPopulatedMap() throws UnsupportedOperationException {
77     return "z";
78   }
79 
80   @Override
getValueNotInPopulatedMap()81   protected String getValueNotInPopulatedMap() throws UnsupportedOperationException {
82     return "26";
83   }
84 
85   /** Helper assertion comparing two maps */
assertMapsEqual(Map<?, ?> expected, Map<?, ?> map)86   private void assertMapsEqual(Map<?, ?> expected, Map<?, ?> map) {
87     assertEquals(expected, map);
88     assertEquals(expected.hashCode(), map.hashCode());
89     assertEquals(expected.entrySet(), map.entrySet());
90 
91     // Assert that expectedValues > mapValues and that
92     // mapValues > expectedValues; i.e. that expectedValues == mapValues.
93     Collection<?> expectedValues = expected.values();
94     Collection<?> mapValues = map.values();
95     assertEquals(expectedValues.size(), mapValues.size());
96     assertTrue(expectedValues.containsAll(mapValues));
97     assertTrue(mapValues.containsAll(expectedValues));
98   }
99 
testTransformEmptyMapEquality()100   public void testTransformEmptyMapEquality() {
101     Map<String, String> map =
102         Maps.transformValues(ImmutableMap.<String, Integer>of(), Functions.toStringFunction());
103     assertMapsEqual(Maps.newHashMap(), map);
104   }
105 
testTransformSingletonMapEquality()106   public void testTransformSingletonMapEquality() {
107     Map<String, String> map =
108         Maps.transformValues(ImmutableMap.of("a", 1), Functions.toStringFunction());
109     Map<String, String> expected = ImmutableMap.of("a", "1");
110     assertMapsEqual(expected, map);
111     assertEquals(expected.get("a"), map.get("a"));
112   }
113 
testTransformIdentityFunctionEquality()114   public void testTransformIdentityFunctionEquality() {
115     Map<String, Integer> underlying = ImmutableMap.of("a", 1);
116     Map<String, Integer> map = Maps.transformValues(underlying, Functions.<Integer>identity());
117     assertMapsEqual(underlying, map);
118   }
119 
testTransformPutEntryIsUnsupported()120   public void testTransformPutEntryIsUnsupported() {
121     Map<String, String> map =
122         Maps.transformValues(ImmutableMap.of("a", 1), Functions.toStringFunction());
123     try {
124       map.put("b", "2");
125       fail();
126     } catch (UnsupportedOperationException expected) {
127     }
128 
129     try {
130       map.putAll(ImmutableMap.of("b", "2"));
131       fail();
132     } catch (UnsupportedOperationException expected) {
133     }
134 
135     try {
136       map.entrySet().iterator().next().setValue("one");
137       fail();
138     } catch (UnsupportedOperationException expected) {
139     }
140   }
141 
testTransformRemoveEntry()142   public void testTransformRemoveEntry() {
143     Map<String, Integer> underlying = Maps.newHashMap();
144     underlying.put("a", 1);
145     Map<String, String> map = Maps.transformValues(underlying, Functions.toStringFunction());
146     assertEquals("1", map.remove("a"));
147     assertNull(map.remove("b"));
148   }
149 
testTransformEqualityOfMapsWithNullValues()150   public void testTransformEqualityOfMapsWithNullValues() {
151     Map<String, String> underlying = Maps.newHashMap();
152     underlying.put("a", null);
153     underlying.put("b", "");
154 
155     Map<String, Boolean> map =
156         Maps.transformValues(
157             underlying,
158             new Function<String, Boolean>() {
159               @Override
160               public Boolean apply(@Nullable String from) {
161                 return from == null;
162               }
163             });
164     Map<String, Boolean> expected = ImmutableMap.of("a", true, "b", false);
165     assertMapsEqual(expected, map);
166     assertEquals(expected.get("a"), map.get("a"));
167     assertEquals(expected.containsKey("a"), map.containsKey("a"));
168     assertEquals(expected.get("b"), map.get("b"));
169     assertEquals(expected.containsKey("b"), map.containsKey("b"));
170     assertEquals(expected.get("c"), map.get("c"));
171     assertEquals(expected.containsKey("c"), map.containsKey("c"));
172   }
173 
testTransformReflectsUnderlyingMap()174   public void testTransformReflectsUnderlyingMap() {
175     Map<String, Integer> underlying = Maps.newHashMap();
176     underlying.put("a", 1);
177     underlying.put("b", 2);
178     underlying.put("c", 3);
179     Map<String, String> map = Maps.transformValues(underlying, Functions.toStringFunction());
180     assertEquals(underlying.size(), map.size());
181 
182     underlying.put("d", 4);
183     assertEquals(underlying.size(), map.size());
184     assertEquals("4", map.get("d"));
185 
186     underlying.remove("c");
187     assertEquals(underlying.size(), map.size());
188     assertFalse(map.containsKey("c"));
189 
190     underlying.clear();
191     assertEquals(underlying.size(), map.size());
192   }
193 
testTransformChangesAreReflectedInUnderlyingMap()194   public void testTransformChangesAreReflectedInUnderlyingMap() {
195     Map<String, Integer> underlying = Maps.newLinkedHashMap();
196     underlying.put("a", 1);
197     underlying.put("b", 2);
198     underlying.put("c", 3);
199     underlying.put("d", 4);
200     underlying.put("e", 5);
201     underlying.put("f", 6);
202     underlying.put("g", 7);
203     Map<String, String> map = Maps.transformValues(underlying, Functions.toStringFunction());
204 
205     map.remove("a");
206     assertFalse(underlying.containsKey("a"));
207 
208     Set<String> keys = map.keySet();
209     keys.remove("b");
210     assertFalse(underlying.containsKey("b"));
211 
212     Iterator<String> keyIterator = keys.iterator();
213     keyIterator.next();
214     keyIterator.remove();
215     assertFalse(underlying.containsKey("c"));
216 
217     Collection<String> values = map.values();
218     values.remove("4");
219     assertFalse(underlying.containsKey("d"));
220 
221     Iterator<String> valueIterator = values.iterator();
222     valueIterator.next();
223     valueIterator.remove();
224     assertFalse(underlying.containsKey("e"));
225 
226     Set<Entry<String, String>> entries = map.entrySet();
227     Entry<String, String> firstEntry = entries.iterator().next();
228     entries.remove(firstEntry);
229     assertFalse(underlying.containsKey("f"));
230 
231     Iterator<Entry<String, String>> entryIterator = entries.iterator();
232     entryIterator.next();
233     entryIterator.remove();
234     assertFalse(underlying.containsKey("g"));
235 
236     assertTrue(underlying.isEmpty());
237     assertTrue(map.isEmpty());
238     assertTrue(keys.isEmpty());
239     assertTrue(values.isEmpty());
240     assertTrue(entries.isEmpty());
241   }
242 
testTransformEquals()243   public void testTransformEquals() {
244     Map<String, Integer> underlying = ImmutableMap.of("a", 0, "b", 1, "c", 2);
245     Map<String, Integer> expected = Maps.transformValues(underlying, Functions.<Integer>identity());
246 
247     assertMapsEqual(expected, expected);
248 
249     Map<String, Integer> equalToUnderlying = Maps.newTreeMap();
250     equalToUnderlying.putAll(underlying);
251     Map<String, Integer> map =
252         Maps.transformValues(equalToUnderlying, Functions.<Integer>identity());
253     assertMapsEqual(expected, map);
254 
255     map =
256         Maps.transformValues(
257             ImmutableMap.of("a", 1, "b", 2, "c", 3),
258             new Function<Integer, Integer>() {
259               @Override
260               public Integer apply(Integer from) {
261                 return from - 1;
262               }
263             });
264     assertMapsEqual(expected, map);
265   }
266 
testTransformEntrySetContains()267   public void testTransformEntrySetContains() {
268     Map<String, Boolean> underlying = Maps.newHashMap();
269     underlying.put("a", null);
270     underlying.put("b", true);
271     underlying.put(null, true);
272 
273     Map<String, Boolean> map =
274         Maps.transformValues(
275             underlying,
276             new Function<Boolean, Boolean>() {
277               @Override
278               public Boolean apply(@Nullable Boolean from) {
279                 return (from == null) ? true : null;
280               }
281             });
282 
283     Set<Entry<String, Boolean>> entries = map.entrySet();
284     assertTrue(entries.contains(Maps.immutableEntry("a", true)));
285     assertTrue(entries.contains(Maps.immutableEntry("b", (Boolean) null)));
286     assertTrue(entries.contains(Maps.immutableEntry((String) null, (Boolean) null)));
287 
288     assertFalse(entries.contains(Maps.immutableEntry("c", (Boolean) null)));
289     assertFalse(entries.contains(Maps.immutableEntry((String) null, true)));
290   }
291 
292   @Override
testKeySetRemoveAllNullFromEmpty()293   public void testKeySetRemoveAllNullFromEmpty() {
294     try {
295       super.testKeySetRemoveAllNullFromEmpty();
296     } catch (RuntimeException tolerated) {
297       // GWT's HashMap.keySet().removeAll(null) doesn't throws NPE.
298     }
299   }
300 
301   @Override
testEntrySetRemoveAllNullFromEmpty()302   public void testEntrySetRemoveAllNullFromEmpty() {
303     try {
304       super.testEntrySetRemoveAllNullFromEmpty();
305     } catch (RuntimeException tolerated) {
306       // GWT's HashMap.entrySet().removeAll(null) doesn't throws NPE.
307     }
308   }
309 }
310