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 static org.junit.contrib.truth.Truth.ASSERT;
20
21import com.google.common.annotations.GwtCompatible;
22import com.google.common.annotations.GwtIncompatible;
23import com.google.common.base.Joiner;
24import com.google.common.collect.ImmutableBiMap.Builder;
25import com.google.common.collect.testing.MapInterfaceTest;
26import com.google.common.collect.testing.ReserializingTestSetGenerator;
27import com.google.common.collect.testing.SetTestSuiteBuilder;
28import com.google.common.collect.testing.features.CollectionFeature;
29import com.google.common.collect.testing.features.CollectionSize;
30import com.google.common.collect.testing.google.BiMapGenerators.ImmutableBiMapEntrySetGenerator;
31import com.google.common.collect.testing.google.BiMapGenerators.ImmutableBiMapInverseEntrySetGenerator;
32import com.google.common.collect.testing.google.BiMapGenerators.ImmutableBiMapInverseKeySetGenerator;
33import com.google.common.collect.testing.google.BiMapGenerators.ImmutableBiMapInverseValuesGenerator;
34import com.google.common.collect.testing.google.BiMapGenerators.ImmutableBiMapKeySetGenerator;
35import com.google.common.collect.testing.google.BiMapGenerators.ImmutableBiMapValuesGenerator;
36import com.google.common.testing.SerializableTester;
37
38import junit.framework.Test;
39import junit.framework.TestCase;
40import junit.framework.TestSuite;
41
42import java.util.Collections;
43import java.util.LinkedHashMap;
44import java.util.Map;
45import java.util.Map.Entry;
46import java.util.Set;
47
48/**
49 * Tests for {@link ImmutableBiMap}.
50 *
51 * @author Jared Levy
52 */
53@GwtCompatible(emulated = true)
54public class ImmutableBiMapTest extends TestCase {
55
56  // TODO: Reduce duplication of ImmutableMapTest code
57
58  @GwtIncompatible("suite")
59  public static Test suite() {
60    TestSuite suite = new TestSuite();
61
62    suite.addTestSuite(MapTests.class);
63    suite.addTestSuite(InverseMapTests.class);
64    suite.addTestSuite(CreationTests.class);
65    suite.addTestSuite(BiMapSpecificTests.class);
66
67    suite.addTest(SetTestSuiteBuilder.using(new ImmutableBiMapKeySetGenerator())
68        .withFeatures(CollectionSize.ANY, CollectionFeature.KNOWN_ORDER,
69            CollectionFeature.ALLOWS_NULL_QUERIES)
70        .named("ImmutableBiMap.keySet")
71        .createTestSuite());
72
73    suite.addTest(SetTestSuiteBuilder.using(
74        new ImmutableBiMapEntrySetGenerator())
75        .withFeatures(CollectionSize.ANY, CollectionFeature.KNOWN_ORDER,
76            CollectionFeature.ALLOWS_NULL_QUERIES)
77        .named("ImmutableBiMap.entrySet")
78        .createTestSuite());
79
80    suite.addTest(SetTestSuiteBuilder.using(new ImmutableBiMapValuesGenerator())
81        .withFeatures(
82            CollectionSize.ANY,
83            CollectionFeature.REJECTS_DUPLICATES_AT_CREATION,
84            CollectionFeature.KNOWN_ORDER,
85            CollectionFeature.ALLOWS_NULL_QUERIES)
86        .named("ImmutableBiMap.values")
87        .createTestSuite());
88
89    suite.addTest(SetTestSuiteBuilder.using(
90        new ImmutableBiMapInverseKeySetGenerator())
91        .withFeatures(
92            CollectionSize.ANY,
93            CollectionFeature.REJECTS_DUPLICATES_AT_CREATION,
94            CollectionFeature.KNOWN_ORDER,
95            CollectionFeature.ALLOWS_NULL_QUERIES)
96        .named("ImmutableBiMap.inverse.keys")
97        .createTestSuite());
98
99    suite.addTest(SetTestSuiteBuilder.using(
100        new ImmutableBiMapInverseEntrySetGenerator())
101        .withFeatures(CollectionSize.ANY, CollectionFeature.KNOWN_ORDER,
102            CollectionFeature.ALLOWS_NULL_QUERIES)
103        .named("ImmutableBiMap.inverse.entrySet")
104        .createTestSuite());
105
106    suite.addTest(SetTestSuiteBuilder.using(
107        new ImmutableBiMapInverseValuesGenerator())
108        .withFeatures(CollectionSize.ANY, CollectionFeature.KNOWN_ORDER,
109            CollectionFeature.ALLOWS_NULL_QUERIES)
110        .named("ImmutableBiMap.inverse.values")
111        .createTestSuite());
112
113    suite.addTest(SetTestSuiteBuilder.using(
114        ReserializingTestSetGenerator.newInstance(
115            new ImmutableBiMapKeySetGenerator()))
116        .withFeatures(CollectionSize.ANY, CollectionFeature.KNOWN_ORDER,
117            CollectionFeature.ALLOWS_NULL_QUERIES)
118        .named("ImmutableBiMap.keySet, reserialized")
119        .createTestSuite());
120
121    suite.addTest(SetTestSuiteBuilder.using(
122        ReserializingTestSetGenerator.newInstance(
123            new ImmutableBiMapEntrySetGenerator()))
124        .withFeatures(CollectionSize.ANY, CollectionFeature.KNOWN_ORDER,
125            CollectionFeature.ALLOWS_NULL_QUERIES)
126        .named("ImmutableBiMap.entrySet, reserialized")
127        .createTestSuite());
128
129    suite.addTest(SetTestSuiteBuilder.using(
130        ReserializingTestSetGenerator.newInstance(
131            new ImmutableBiMapValuesGenerator()))
132        .withFeatures(
133            CollectionSize.ANY,
134            CollectionFeature.REJECTS_DUPLICATES_AT_CREATION,
135            CollectionFeature.KNOWN_ORDER,
136            CollectionFeature.ALLOWS_NULL_QUERIES)
137        .named("ImmutableBiMap.values, reserialized")
138        .createTestSuite());
139
140    return suite;
141  }
142
143  public static abstract class AbstractMapTests<K, V>
144      extends MapInterfaceTest<K, V> {
145    public AbstractMapTests() {
146      super(false, false, false, false, false);
147    }
148
149    @Override protected Map<K, V> makeEmptyMap() {
150      throw new UnsupportedOperationException();
151    }
152
153    private static final Joiner joiner = Joiner.on(", ");
154
155    @Override protected void assertMoreInvariants(Map<K, V> map) {
156
157      BiMap<K, V> bimap = (BiMap<K, V>) map;
158
159      for (Entry<K, V> entry : map.entrySet()) {
160        assertEquals(entry.getKey() + "=" + entry.getValue(),
161            entry.toString());
162        assertEquals(entry.getKey(), bimap.inverse().get(entry.getValue()));
163      }
164
165      assertEquals("{" + joiner.join(map.entrySet()) + "}",
166          map.toString());
167      assertEquals("[" + joiner.join(map.entrySet()) + "]",
168          map.entrySet().toString());
169      assertEquals("[" + joiner.join(map.keySet()) + "]",
170          map.keySet().toString());
171      assertEquals("[" + joiner.join(map.values()) + "]",
172          map.values().toString());
173
174      assertEquals(Sets.newHashSet(map.entrySet()), map.entrySet());
175      assertEquals(Sets.newHashSet(map.keySet()), map.keySet());
176    }
177  }
178
179  public static class MapTests extends AbstractMapTests<String, Integer> {
180    @Override protected Map<String, Integer> makeEmptyMap() {
181      return ImmutableBiMap.of();
182    }
183
184    @Override protected Map<String, Integer> makePopulatedMap() {
185      return ImmutableBiMap.of("one", 1, "two", 2, "three", 3);
186    }
187
188    @Override protected String getKeyNotInPopulatedMap() {
189      return "minus one";
190    }
191
192    @Override protected Integer getValueNotInPopulatedMap() {
193      return -1;
194    }
195  }
196
197  public static class InverseMapTests
198      extends AbstractMapTests<String, Integer> {
199    @Override protected Map<String, Integer> makeEmptyMap() {
200      return ImmutableBiMap.of();
201    }
202
203    @Override protected Map<String, Integer> makePopulatedMap() {
204      return ImmutableBiMap.of(1, "one", 2, "two", 3, "three").inverse();
205    }
206
207    @Override protected String getKeyNotInPopulatedMap() {
208      return "minus one";
209    }
210
211    @Override protected Integer getValueNotInPopulatedMap() {
212      return -1;
213    }
214  }
215
216  public static class CreationTests extends TestCase {
217    public void testEmptyBuilder() {
218      ImmutableBiMap<String, Integer> map
219          = new Builder<String, Integer>().build();
220      assertEquals(Collections.<String, Integer>emptyMap(), map);
221      assertEquals(Collections.<Integer, String>emptyMap(), map.inverse());
222      assertSame(ImmutableBiMap.of(), map);
223    }
224
225    public void testSingletonBuilder() {
226      ImmutableBiMap<String, Integer> map = new Builder<String, Integer>()
227          .put("one", 1)
228          .build();
229      assertMapEquals(map, "one", 1);
230      assertMapEquals(map.inverse(), 1, "one");
231    }
232
233    public void testBuilder() {
234      ImmutableBiMap<String, Integer> map
235          = ImmutableBiMap.<String, Integer>builder()
236            .put("one", 1)
237            .put("two", 2)
238            .put("three", 3)
239            .put("four", 4)
240            .put("five", 5)
241            .build();
242      assertMapEquals(map,
243          "one", 1, "two", 2, "three", 3, "four", 4, "five", 5);
244      assertMapEquals(map.inverse(),
245          1, "one", 2, "two", 3, "three", 4, "four", 5, "five");
246    }
247
248    public void testBuilderPutAllWithEmptyMap() {
249      ImmutableBiMap<String, Integer> map = new Builder<String, Integer>()
250          .putAll(Collections.<String, Integer>emptyMap())
251          .build();
252      assertEquals(Collections.<String, Integer>emptyMap(), map);
253    }
254
255    public void testBuilderPutAll() {
256      Map<String, Integer> toPut = new LinkedHashMap<String, Integer>();
257      toPut.put("one", 1);
258      toPut.put("two", 2);
259      toPut.put("three", 3);
260      Map<String, Integer> moreToPut = new LinkedHashMap<String, Integer>();
261      moreToPut.put("four", 4);
262      moreToPut.put("five", 5);
263
264      ImmutableBiMap<String, Integer> map = new Builder<String, Integer>()
265          .putAll(toPut)
266          .putAll(moreToPut)
267          .build();
268      assertMapEquals(map,
269          "one", 1, "two", 2, "three", 3, "four", 4, "five", 5);
270      assertMapEquals(map.inverse(),
271          1, "one", 2, "two", 3, "three", 4, "four", 5, "five");
272    }
273
274    public void testBuilderReuse() {
275      Builder<String, Integer> builder = new Builder<String, Integer>();
276      ImmutableBiMap<String, Integer> mapOne = builder
277          .put("one", 1)
278          .put("two", 2)
279          .build();
280      ImmutableBiMap<String, Integer> mapTwo = builder
281          .put("three", 3)
282          .put("four", 4)
283          .build();
284
285      assertMapEquals(mapOne, "one", 1, "two", 2);
286      assertMapEquals(mapOne.inverse(), 1, "one", 2, "two");
287      assertMapEquals(mapTwo, "one", 1, "two", 2, "three", 3, "four", 4);
288      assertMapEquals(mapTwo.inverse(),
289          1, "one", 2, "two", 3, "three", 4, "four");
290    }
291
292    public void testBuilderPutNullKey() {
293      Builder<String, Integer> builder = new Builder<String, Integer>();
294      try {
295        builder.put(null, 1);
296        fail();
297      } catch (NullPointerException expected) {
298      }
299    }
300
301    public void testBuilderPutNullValue() {
302      Builder<String, Integer> builder = new Builder<String, Integer>();
303      try {
304        builder.put("one", null);
305        fail();
306      } catch (NullPointerException expected) {
307      }
308    }
309
310    public void testBuilderPutNullKeyViaPutAll() {
311      Builder<String, Integer> builder = new Builder<String, Integer>();
312      try {
313        builder.putAll(Collections.<String, Integer>singletonMap(null, 1));
314        fail();
315      } catch (NullPointerException expected) {
316      }
317    }
318
319    public void testBuilderPutNullValueViaPutAll() {
320      Builder<String, Integer> builder = new Builder<String, Integer>();
321      try {
322        builder.putAll(Collections.<String, Integer>singletonMap("one", null));
323        fail();
324      } catch (NullPointerException expected) {
325      }
326    }
327
328    public void testPuttingTheSameKeyTwiceThrowsOnBuild() {
329      Builder<String, Integer> builder = new Builder<String, Integer>()
330          .put("one", 1)
331          .put("one", 1); // throwing on this line would be even better
332
333      try {
334        builder.build();
335        fail();
336      } catch (IllegalArgumentException expected) {
337        assertEquals("duplicate key: one", expected.getMessage());
338      }
339    }
340
341    public void testOf() {
342      assertMapEquals(
343          ImmutableBiMap.of("one", 1),
344          "one", 1);
345      assertMapEquals(
346          ImmutableBiMap.of("one", 1).inverse(),
347          1, "one");
348      assertMapEquals(
349          ImmutableBiMap.of("one", 1, "two", 2),
350          "one", 1, "two", 2);
351      assertMapEquals(
352          ImmutableBiMap.of("one", 1, "two", 2).inverse(),
353          1, "one", 2, "two");
354      assertMapEquals(
355          ImmutableBiMap.of("one", 1, "two", 2, "three", 3),
356          "one", 1, "two", 2, "three", 3);
357      assertMapEquals(
358          ImmutableBiMap.of("one", 1, "two", 2, "three", 3).inverse(),
359          1, "one", 2, "two", 3, "three");
360      assertMapEquals(
361          ImmutableBiMap.of("one", 1, "two", 2, "three", 3, "four", 4),
362          "one", 1, "two", 2, "three", 3, "four", 4);
363      assertMapEquals(
364          ImmutableBiMap.of(
365              "one", 1, "two", 2, "three", 3, "four", 4).inverse(),
366          1, "one", 2, "two", 3, "three", 4, "four");
367      assertMapEquals(
368          ImmutableBiMap.of(
369              "one", 1, "two", 2, "three", 3, "four", 4, "five", 5),
370          "one", 1, "two", 2, "three", 3, "four", 4, "five", 5);
371      assertMapEquals(
372          ImmutableBiMap.of(
373              "one", 1, "two", 2, "three", 3, "four", 4, "five", 5).inverse(),
374          1, "one", 2, "two", 3, "three", 4, "four", 5, "five");
375    }
376
377    public void testOfNullKey() {
378      try {
379        ImmutableBiMap.of(null, 1);
380        fail();
381      } catch (NullPointerException expected) {
382      }
383
384      try {
385        ImmutableBiMap.of("one", 1, null, 2);
386        fail();
387      } catch (NullPointerException expected) {
388      }
389    }
390
391    public void testOfNullValue() {
392      try {
393        ImmutableBiMap.of("one", null);
394        fail();
395      } catch (NullPointerException expected) {
396      }
397
398      try {
399        ImmutableBiMap.of("one", 1, "two", null);
400        fail();
401      } catch (NullPointerException expected) {
402      }
403    }
404
405    public void testOfWithDuplicateKey() {
406      try {
407        ImmutableBiMap.of("one", 1, "one", 1);
408        fail();
409      } catch (IllegalArgumentException expected) {
410        assertEquals("duplicate key: one", expected.getMessage());
411      }
412    }
413
414    public void testCopyOfEmptyMap() {
415      ImmutableBiMap<String, Integer> copy
416          = ImmutableBiMap.copyOf(Collections.<String, Integer>emptyMap());
417      assertEquals(Collections.<String, Integer>emptyMap(), copy);
418      assertSame(copy, ImmutableBiMap.copyOf(copy));
419      assertSame(ImmutableBiMap.of(), copy);
420    }
421
422    public void testCopyOfSingletonMap() {
423      ImmutableBiMap<String, Integer> copy
424          = ImmutableBiMap.copyOf(Collections.singletonMap("one", 1));
425      assertMapEquals(copy, "one", 1);
426      assertSame(copy, ImmutableBiMap.copyOf(copy));
427    }
428
429    public void testCopyOf() {
430      Map<String, Integer> original = new LinkedHashMap<String, Integer>();
431      original.put("one", 1);
432      original.put("two", 2);
433      original.put("three", 3);
434
435      ImmutableBiMap<String, Integer> copy = ImmutableBiMap.copyOf(original);
436      assertMapEquals(copy, "one", 1, "two", 2, "three", 3);
437      assertSame(copy, ImmutableBiMap.copyOf(copy));
438    }
439
440    public void testEmpty() {
441      ImmutableBiMap<String, Integer> bimap = ImmutableBiMap.of();
442      assertEquals(Collections.<String, Integer>emptyMap(), bimap);
443      assertEquals(Collections.<String, Integer>emptyMap(), bimap.inverse());
444    }
445
446    public void testFromHashMap() {
447      Map<String, Integer> hashMap = Maps.newLinkedHashMap();
448      hashMap.put("one", 1);
449      hashMap.put("two", 2);
450      ImmutableBiMap<String, Integer> bimap = ImmutableBiMap.copyOf(
451          ImmutableMap.of("one", 1, "two", 2));
452      assertMapEquals(bimap, "one", 1, "two", 2);
453      assertMapEquals(bimap.inverse(), 1, "one", 2, "two");
454    }
455
456    public void testFromImmutableMap() {
457      ImmutableBiMap<String, Integer> bimap = ImmutableBiMap.copyOf(
458          new ImmutableMap.Builder<String, Integer>()
459              .put("one", 1)
460              .put("two", 2)
461              .put("three", 3)
462              .put("four", 4)
463              .put("five", 5)
464              .build());
465      assertMapEquals(bimap,
466          "one", 1, "two", 2, "three", 3, "four", 4, "five", 5);
467      assertMapEquals(bimap.inverse(),
468          1, "one", 2, "two", 3, "three", 4, "four", 5, "five");
469    }
470
471    public void testDuplicateValues() {
472      ImmutableMap<String, Integer> map
473          = new ImmutableMap.Builder<String, Integer>()
474              .put("one", 1)
475              .put("two", 2)
476              .put("uno", 1)
477              .put("dos", 2)
478              .build();
479
480      try {
481        ImmutableBiMap.copyOf(map);
482        fail();
483      } catch (IllegalArgumentException expected) {
484        assertEquals("duplicate key: 1", expected.getMessage());
485      }
486    }
487  }
488
489  public static class BiMapSpecificTests extends TestCase {
490
491    public void testForcePut() {
492      ImmutableBiMap<String, Integer> bimap = ImmutableBiMap.copyOf(
493          ImmutableMap.of("one", 1, "two", 2));
494      try {
495        bimap.forcePut("three", 3);
496        fail();
497      } catch (UnsupportedOperationException expected) {}
498    }
499
500    public void testKeySet() {
501      ImmutableBiMap<String, Integer> bimap = ImmutableBiMap.copyOf(
502          ImmutableMap.of("one", 1, "two", 2, "three", 3, "four", 4));
503      Set<String> keys = bimap.keySet();
504      assertEquals(Sets.newHashSet("one", "two", "three", "four"), keys);
505      ASSERT.that(keys).hasContentsInOrder("one", "two", "three", "four");
506    }
507
508    public void testValues() {
509      ImmutableBiMap<String, Integer> bimap = ImmutableBiMap.copyOf(
510          ImmutableMap.of("one", 1, "two", 2, "three", 3, "four", 4));
511      Set<Integer> values = bimap.values();
512      assertEquals(Sets.newHashSet(1, 2, 3, 4), values);
513      ASSERT.that(values).hasContentsInOrder(1, 2, 3, 4);
514    }
515
516    public void testDoubleInverse() {
517      ImmutableBiMap<String, Integer> bimap = ImmutableBiMap.copyOf(
518          ImmutableMap.of("one", 1, "two", 2));
519      assertSame(bimap, bimap.inverse().inverse());
520    }
521
522    @GwtIncompatible("SerializableTester")
523    public void testEmptySerialization() {
524      ImmutableBiMap<String, Integer> bimap = ImmutableBiMap.of();
525      assertSame(bimap, SerializableTester.reserializeAndAssert(bimap));
526    }
527
528    @GwtIncompatible("SerializableTester")
529    public void testSerialization() {
530      ImmutableBiMap<String, Integer> bimap = ImmutableBiMap.copyOf(
531          ImmutableMap.of("one", 1, "two", 2));
532      ImmutableBiMap<String, Integer> copy =
533          SerializableTester.reserializeAndAssert(bimap);
534      assertEquals(Integer.valueOf(1), copy.get("one"));
535      assertEquals("one", copy.inverse().get(1));
536      assertSame(copy, copy.inverse().inverse());
537    }
538
539    @GwtIncompatible("SerializableTester")
540    public void testInverseSerialization() {
541      ImmutableBiMap<String, Integer> bimap = ImmutableBiMap.copyOf(
542          ImmutableMap.of(1, "one", 2, "two")).inverse();
543      ImmutableBiMap<String, Integer> copy =
544          SerializableTester.reserializeAndAssert(bimap);
545      assertEquals(Integer.valueOf(1), copy.get("one"));
546      assertEquals("one", copy.inverse().get(1));
547      assertSame(copy, copy.inverse().inverse());
548    }
549  }
550
551  private static <K, V> void assertMapEquals(Map<K, V> map,
552      Object... alternatingKeysAndValues) {
553    int i = 0;
554    for (Entry<K, V> entry : map.entrySet()) {
555      assertEquals(alternatingKeysAndValues[i++], entry.getKey());
556      assertEquals(alternatingKeysAndValues[i++], entry.getValue());
557    }
558  }
559}
560