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