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