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 org.truth0.Truth.ASSERT;
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 
35 import junit.framework.Test;
36 import junit.framework.TestCase;
37 import junit.framework.TestSuite;
38 
39 import java.util.Collections;
40 import java.util.Iterator;
41 import java.util.List;
42 import java.util.Map;
43 import java.util.Map.Entry;
44 import java.util.Set;
45 
46 /**
47  * Tests for {@code EnumBiMap}.
48  *
49  * @author Mike Bostock
50  * @author Jared Levy
51  */
52 @GwtCompatible(emulated = true)
53 public 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
create(Object... entries)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
samples()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
createArray(int length)81     public Entry<Country, Currency>[] createArray(int length) {
82       return new Entry[length];
83     }
84 
85     @Override
order(List<Entry<Country, Currency>> insertionOrder)86     public Iterable<Entry<Country, Currency>> order(List<Entry<Country, Currency>> insertionOrder) {
87       return orderEntriesByKey(insertionOrder);
88     }
89 
90     @Override
createKeyArray(int length)91     public Country[] createKeyArray(int length) {
92       return new Country[length];
93     }
94 
95     @Override
createValueArray(int length)96     public Currency[] createValueArray(int length) {
97       return new Currency[length];
98     }
99   }
100 
101   @GwtIncompatible("suite")
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 
testCreate()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 
testCreateFromMap()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 
testEnumBiMapConstructor()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 
testKeyType()177   public void testKeyType() {
178     EnumBiMap<Currency, Country> bimap =
179         EnumBiMap.create(Currency.class, Country.class);
180     assertEquals(Currency.class, bimap.keyType());
181   }
182 
testValueType()183   public void testValueType() {
184     EnumBiMap<Currency, Country> bimap =
185         EnumBiMap.create(Currency.class, Country.class);
186     assertEquals(Country.class, bimap.valueType());
187   }
188 
testIterationOrder()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 
testKeySetIteratorRemove()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 
testValuesIteratorRemove()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 
testEntrySet()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")
testSerializable()280   public void testSerializable() {
281     SerializableTester.reserializeAndAssert(
282         EnumBiMap.create(ImmutableMap.of(Currency.DOLLAR, Country.CANADA)));
283   }
284 
285   @GwtIncompatible("reflection")
testNulls()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 
testEquals()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