1/*
2 * Copyright (C) 2007 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 com.google.common.collect.testing.Helpers.orderEntriesByKey;
20import static org.truth0.Truth.ASSERT;
21
22import com.google.common.annotations.GwtCompatible;
23import com.google.common.annotations.GwtIncompatible;
24import com.google.common.collect.testing.Helpers;
25import com.google.common.collect.testing.SampleElements;
26import com.google.common.collect.testing.features.CollectionFeature;
27import com.google.common.collect.testing.features.CollectionSize;
28import com.google.common.collect.testing.features.MapFeature;
29import com.google.common.collect.testing.google.BiMapTestSuiteBuilder;
30import com.google.common.collect.testing.google.TestBiMapGenerator;
31import com.google.common.testing.EqualsTester;
32import com.google.common.testing.NullPointerTester;
33import com.google.common.testing.SerializableTester;
34
35import junit.framework.Test;
36import junit.framework.TestCase;
37import junit.framework.TestSuite;
38
39import java.util.Collections;
40import java.util.Iterator;
41import java.util.List;
42import java.util.Map;
43import java.util.Map.Entry;
44import java.util.Set;
45
46/**
47 * Tests for {@code EnumBiMap}.
48 *
49 * @author Mike Bostock
50 * @author Jared Levy
51 */
52@GwtCompatible(emulated = true)
53public class EnumBiMapTest extends TestCase {
54  private enum Currency { DOLLAR, FRANC, PESO, POUND, YEN }
55  private enum Country { CANADA, CHILE, JAPAN, SWITZERLAND, UK }
56
57  public static final class EnumBiMapGenerator implements TestBiMapGenerator<Country, Currency> {
58    @SuppressWarnings("unchecked")
59    @Override
60    public BiMap<Country, Currency> create(Object... entries) {
61      BiMap<Country, Currency> result = EnumBiMap.create(Country.class, Currency.class);
62      for (Object object : entries) {
63        Entry<Country, Currency> entry = (Entry<Country, Currency>) object;
64        result.put(entry.getKey(), entry.getValue());
65      }
66      return result;
67    }
68
69    @Override
70    public SampleElements<Entry<Country, Currency>> samples() {
71      return new SampleElements<Entry<Country, Currency>>(
72          Helpers.mapEntry(Country.CANADA, Currency.DOLLAR),
73          Helpers.mapEntry(Country.CHILE, Currency.PESO),
74          Helpers.mapEntry(Country.UK, Currency.POUND),
75          Helpers.mapEntry(Country.JAPAN, Currency.YEN),
76          Helpers.mapEntry(Country.SWITZERLAND, Currency.FRANC));
77    }
78
79    @SuppressWarnings("unchecked")
80    @Override
81    public Entry<Country, Currency>[] createArray(int length) {
82      return new Entry[length];
83    }
84
85    @Override
86    public Iterable<Entry<Country, Currency>> order(List<Entry<Country, Currency>> insertionOrder) {
87      return orderEntriesByKey(insertionOrder);
88    }
89
90    @Override
91    public Country[] createKeyArray(int length) {
92      return new Country[length];
93    }
94
95    @Override
96    public Currency[] createValueArray(int length) {
97      return new Currency[length];
98    }
99  }
100
101  @GwtIncompatible("suite")
102  public static Test suite() {
103    TestSuite suite = new TestSuite();
104    suite.addTest(BiMapTestSuiteBuilder.using(new EnumBiMapGenerator())
105        .named("EnumBiMap")
106        .withFeatures(CollectionSize.ANY,
107            CollectionFeature.SERIALIZABLE,
108            CollectionFeature.SUPPORTS_ITERATOR_REMOVE,
109            MapFeature.GENERAL_PURPOSE,
110            CollectionFeature.KNOWN_ORDER)
111        .createTestSuite());
112    suite.addTestSuite(EnumBiMapTest.class);
113    return suite;
114  }
115
116  public void testCreate() {
117    EnumBiMap<Currency, Country> bimap =
118        EnumBiMap.create(Currency.class, Country.class);
119    assertTrue(bimap.isEmpty());
120    assertEquals("{}", bimap.toString());
121    assertEquals(HashBiMap.create(), bimap);
122    bimap.put(Currency.DOLLAR, Country.CANADA);
123    assertEquals(Country.CANADA, bimap.get(Currency.DOLLAR));
124    assertEquals(Currency.DOLLAR, bimap.inverse().get(Country.CANADA));
125  }
126
127  public void testCreateFromMap() {
128    /* Test with non-empty Map. */
129    Map<Currency, Country> map = ImmutableMap.of(
130        Currency.DOLLAR, Country.CANADA,
131        Currency.PESO, Country.CHILE,
132        Currency.FRANC, Country.SWITZERLAND);
133    EnumBiMap<Currency, Country> bimap = EnumBiMap.create(map);
134    assertEquals(Country.CANADA, bimap.get(Currency.DOLLAR));
135    assertEquals(Currency.DOLLAR, bimap.inverse().get(Country.CANADA));
136
137    /* Map must have at least one entry if not an EnumBiMap. */
138    try {
139      EnumBiMap.create(Collections.<Currency, Country>emptyMap());
140      fail("IllegalArgumentException expected");
141    } catch (IllegalArgumentException expected) {}
142    try {
143      EnumBiMap.create(
144          EnumHashBiMap.<Currency, Country>create(Currency.class));
145      fail("IllegalArgumentException expected");
146    } catch (IllegalArgumentException expected) {}
147
148    /* Map can be empty if it's an EnumBiMap. */
149    Map<Currency, Country> emptyBimap =
150        EnumBiMap.create(Currency.class, Country.class);
151    bimap = EnumBiMap.create(emptyBimap);
152    assertTrue(bimap.isEmpty());
153  }
154
155  public void testEnumBiMapConstructor() {
156    /* Test that it copies existing entries. */
157    EnumBiMap<Currency, Country> bimap1 =
158        EnumBiMap.create(Currency.class, Country.class);
159    bimap1.put(Currency.DOLLAR, Country.CANADA);
160    EnumBiMap<Currency, Country> bimap2 =
161        EnumBiMap.create(bimap1);
162    assertEquals(Country.CANADA, bimap2.get(Currency.DOLLAR));
163    assertEquals(bimap1, bimap2);
164    bimap2.inverse().put(Country.SWITZERLAND, Currency.FRANC);
165    assertEquals(Country.SWITZERLAND, bimap2.get(Currency.FRANC));
166    assertNull(bimap1.get(Currency.FRANC));
167    assertFalse(bimap2.equals(bimap1));
168
169    /* Test that it can be empty. */
170    EnumBiMap<Currency, Country> emptyBimap =
171        EnumBiMap.create(Currency.class, Country.class);
172    EnumBiMap<Currency, Country> bimap3 =
173        EnumBiMap.create(emptyBimap);
174    assertEquals(bimap3, emptyBimap);
175  }
176
177  public void testKeyType() {
178    EnumBiMap<Currency, Country> bimap =
179        EnumBiMap.create(Currency.class, Country.class);
180    assertEquals(Currency.class, bimap.keyType());
181  }
182
183  public void testValueType() {
184    EnumBiMap<Currency, Country> bimap =
185        EnumBiMap.create(Currency.class, Country.class);
186    assertEquals(Country.class, bimap.valueType());
187  }
188
189  public void testIterationOrder() {
190    // The enum orderings are alphabetical, leading to the bimap and its inverse
191    // having inconsistent iteration orderings.
192    Map<Currency, Country> map = ImmutableMap.of(
193        Currency.DOLLAR, Country.CANADA,
194        Currency.PESO, Country.CHILE,
195        Currency.FRANC, Country.SWITZERLAND);
196    EnumBiMap<Currency, Country> bimap = EnumBiMap.create(map);
197
198    // forward map ordered by currency
199    ASSERT.that(bimap.keySet())
200        .has().exactly(Currency.DOLLAR, Currency.FRANC, Currency.PESO).inOrder();
201    // forward map ordered by currency (even for country values)
202    ASSERT.that(bimap.values())
203        .has().exactly(Country.CANADA, Country.SWITZERLAND, Country.CHILE).inOrder();
204    // backward map ordered by country
205    ASSERT.that(bimap.inverse().keySet())
206        .has().exactly(Country.CANADA, Country.CHILE, Country.SWITZERLAND).inOrder();
207    // backward map ordered by country (even for currency values)
208    ASSERT.that(bimap.inverse().values())
209        .has().exactly(Currency.DOLLAR, Currency.PESO, Currency.FRANC).inOrder();
210  }
211
212  public void testKeySetIteratorRemove() {
213    // The enum orderings are alphabetical, leading to the bimap and its inverse
214    // having inconsistent iteration orderings.
215    Map<Currency, Country> map = ImmutableMap.of(
216        Currency.DOLLAR, Country.CANADA,
217        Currency.PESO, Country.CHILE,
218        Currency.FRANC, Country.SWITZERLAND);
219    EnumBiMap<Currency, Country> bimap = EnumBiMap.create(map);
220
221    Iterator<Currency> iter = bimap.keySet().iterator();
222    assertEquals(Currency.DOLLAR, iter.next());
223    iter.remove();
224
225    // forward map ordered by currency
226    ASSERT.that(bimap.keySet())
227        .has().exactly(Currency.FRANC, Currency.PESO).inOrder();
228    // forward map ordered by currency (even for country values)
229    ASSERT.that(bimap.values())
230        .has().exactly(Country.SWITZERLAND, Country.CHILE).inOrder();
231    // backward map ordered by country
232    ASSERT.that(bimap.inverse().keySet())
233        .has().exactly(Country.CHILE, Country.SWITZERLAND).inOrder();
234    // backward map ordered by country (even for currency values)
235    ASSERT.that(bimap.inverse().values())
236        .has().exactly(Currency.PESO, Currency.FRANC).inOrder();
237  }
238
239  public void testValuesIteratorRemove() {
240    // The enum orderings are alphabetical, leading to the bimap and its inverse
241    // having inconsistent iteration orderings.
242    Map<Currency, Country> map = ImmutableMap.of(
243        Currency.DOLLAR, Country.CANADA,
244        Currency.PESO, Country.CHILE,
245        Currency.FRANC, Country.SWITZERLAND);
246    EnumBiMap<Currency, Country> bimap = EnumBiMap.create(map);
247
248    Iterator<Currency> iter = bimap.keySet().iterator();
249    assertEquals(Currency.DOLLAR, iter.next());
250    assertEquals(Currency.FRANC, iter.next());
251    iter.remove();
252
253    // forward map ordered by currency
254    ASSERT.that(bimap.keySet())
255        .has().exactly(Currency.DOLLAR, Currency.PESO).inOrder();
256    // forward map ordered by currency (even for country values)
257    ASSERT.that(bimap.values())
258        .has().exactly(Country.CANADA, Country.CHILE).inOrder();
259    // backward map ordered by country
260    ASSERT.that(bimap.inverse().keySet())
261        .has().exactly(Country.CANADA, Country.CHILE).inOrder();
262    // backward map ordered by country (even for currency values)
263    ASSERT.that(bimap.inverse().values())
264        .has().exactly(Currency.DOLLAR, Currency.PESO).inOrder();
265  }
266
267  public void testEntrySet() {
268    // Bug 3168290
269    Map<Currency, Country> map = ImmutableMap.of(
270        Currency.DOLLAR, Country.CANADA,
271        Currency.PESO, Country.CHILE,
272        Currency.FRANC, Country.SWITZERLAND);
273    EnumBiMap<Currency, Country> bimap = EnumBiMap.create(map);
274    Set<Object> uniqueEntries = Sets.newIdentityHashSet();
275    uniqueEntries.addAll(bimap.entrySet());
276    assertEquals(3, uniqueEntries.size());
277  }
278
279  @GwtIncompatible("serialization")
280  public void testSerializable() {
281    SerializableTester.reserializeAndAssert(
282        EnumBiMap.create(ImmutableMap.of(Currency.DOLLAR, Country.CANADA)));
283  }
284
285  @GwtIncompatible("reflection")
286  public void testNulls() {
287    new NullPointerTester().testAllPublicStaticMethods(EnumBiMap.class);
288    new NullPointerTester()
289        .testAllPublicInstanceMethods(
290            EnumBiMap.create(ImmutableMap.of(Currency.DOLLAR, Country.CHILE)));
291  }
292
293  public void testEquals() {
294    new EqualsTester()
295        .addEqualityGroup(
296            EnumBiMap.create(ImmutableMap.of(Currency.DOLLAR, Country.CANADA)),
297            EnumBiMap.create(ImmutableMap.of(Currency.DOLLAR, Country.CANADA)))
298        .addEqualityGroup(EnumBiMap.create(ImmutableMap.of(Currency.DOLLAR, Country.CHILE)))
299        .addEqualityGroup(EnumBiMap.create(ImmutableMap.of(Currency.FRANC, Country.CANADA)))
300        .testEquals();
301  }
302
303  /* Remaining behavior tested by AbstractBiMapTest. */
304}
305