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 java.util.Arrays.asList;
20
21import com.google.common.annotations.GwtCompatible;
22import com.google.common.annotations.GwtIncompatible;
23import com.google.common.testing.SerializableTester;
24
25import junit.framework.TestCase;
26
27import java.io.Serializable;
28import java.util.Collections;
29import java.util.Iterator;
30import java.util.Map;
31import java.util.Map.Entry;
32import java.util.Set;
33
34/**
35 * Common tests for any {@code BiMap}.
36 *
37 * @author Kevin Bourrillion
38 */
39@GwtCompatible(emulated = true)
40public abstract class AbstractBiMapTest extends TestCase {
41
42  protected abstract BiMap<Integer, String> create();
43
44  protected BiMap<Integer, String> bimap;
45  protected Set<Entry<Integer, String>> entrySet;
46
47  // public for GWT
48  @Override public void setUp() throws Exception {
49    super.setUp();
50    bimap = create();
51    entrySet = bimap.entrySet();
52  }
53
54  public void testClear() {
55    bimap.clear();
56    assertTrue(bimap.isEmpty());
57    putOneTwoThree();
58    bimap.clear();
59    assertTrue(bimap.isEmpty());
60  }
61
62  public void testContainsKey() {
63    assertFalse(bimap.containsKey(null));
64    assertFalse(bimap.containsKey(1));
65    assertFalse(bimap.containsKey("one"));
66
67    bimap.put(1, "one");
68    assertTrue(bimap.containsKey(1));
69
70    bimap.put(null, null);
71    assertTrue(bimap.containsKey(null));
72  }
73
74  public void testContainsValue() {
75    assertFalse(bimap.containsValue(null));
76    assertFalse(bimap.containsValue(1));
77    assertFalse(bimap.containsValue("one"));
78
79    bimap.put(1, "one");
80    assertTrue(bimap.containsValue("one"));
81
82    bimap.put(null, null);
83    assertTrue(bimap.containsValue(null));
84  }
85
86  public void testEquals() {
87    BiMap<Integer, String> biMap = create();
88    assertEquals(biMap, biMap);
89    assertEquals(create(), biMap);
90    biMap.put(1, null);
91    assertFalse(create().equals(biMap));
92  }
93
94  public void testGet() {
95    assertNull(bimap.get(1));
96    assertNull(bimap.get(null));
97    assertNull(bimap.get("bad"));
98
99    bimap.put(1, "one");
100    bimap.put(0, null);
101    bimap.put(null, "nothing");
102    assertEquals("one", bimap.get(1));
103    assertNull(bimap.get(0));
104    assertEquals("nothing", bimap.get(null));
105    assertNull(bimap.get("bad"));
106
107    bimap.forcePut(null, null);
108    assertNull(bimap.get(null));
109    bimap.remove(null);
110    assertNull(bimap.get(null));
111  }
112
113  public void testInverseSimple() {
114    BiMap<String, Integer> inverse = bimap.inverse();
115    bimap.put(1, "one");
116    bimap.put(2, "two");
117    assertEquals(ImmutableMap.of("one", 1, "two", 2), inverse);
118    // see InverseBiMapTest
119
120    assertSame(bimap, inverse.inverse());
121  }
122
123  public void testInversePut() {
124    BiMap<String, Integer> inverse = bimap.inverse();
125    bimap.put(1, "one");
126    bimap.inverse().put("two", 2);
127    assertEquals(ImmutableMap.of("one", 1, "two", 2), inverse);
128    assertEquals(ImmutableMap.of(1, "one", 2, "two"), bimap);
129  }
130
131  public void testIsEmpty() {
132    assertTrue(bimap.isEmpty());
133    bimap.put(1, "one");
134    assertFalse(bimap.isEmpty());
135    bimap.remove(1);
136    assertTrue(bimap.isEmpty());
137  }
138
139  public void testPut() {
140    bimap.put(1, "one");
141    assertEquals(ImmutableMap.of(1, "one"), bimap);
142
143    bimap.put(2, "two");
144    assertEquals(ImmutableMap.of(1, "one", 2, "two"), bimap);
145
146    bimap.put(2, "two");
147    assertEquals(ImmutableMap.of(1, "one", 2, "two"), bimap);
148
149    bimap.put(1, "ONE");
150    assertEquals(ImmutableMap.of(1, "ONE", 2, "two"), bimap);
151
152    try {
153      bimap.put(3, "two");
154      fail();
155    } catch (IllegalArgumentException e) {
156    }
157    assertEquals(ImmutableMap.of(1, "ONE", 2, "two"), bimap);
158
159    bimap.put(-1, null);
160    bimap.put(null, "null");
161    Map<Integer, String> expected = Maps.newHashMap();
162    expected.put(1, "ONE");
163    expected.put(2, "two");
164    expected.put(-1, null);
165    expected.put(null, "null");
166
167    assertEquals(expected, bimap);
168
169    bimap.remove(-1);
170    bimap.put(null, null);
171
172    expected.remove(-1);
173    expected.put(null, null);
174
175    assertEquals(expected, bimap);
176  }
177
178  public void testPutNull() {
179    bimap.put(-1, null);
180    assertTrue(bimap.containsValue(null));
181    bimap.put(1, "one");
182    assertTrue(bimap.containsValue(null));
183  }
184
185  public void testPutAll() {
186    bimap.put(1, "one");
187    Map<Integer, String> newEntries = ImmutableMap.of(2, "two", 3, "three");
188    bimap.putAll(newEntries);
189    assertEquals(ImmutableMap.of(1, "one", 2, "two", 3, "three"), bimap);
190  }
191
192  public void testForcePut() {
193    assertNull(bimap.forcePut(1, "one"));
194    assertEquals(ImmutableMap.of(1, "one"), bimap);
195    assertEquals("one", bimap.forcePut(1, "one"));
196    assertEquals(ImmutableMap.of(1, "one"), bimap);
197    assertEquals("one", bimap.forcePut(1, "ONE"));
198    assertEquals(ImmutableMap.of(1, "ONE"), bimap);
199    assertNull(bimap.forcePut(-1, "ONE")); // key 1 disappears without a trace
200    assertEquals(ImmutableMap.of(-1, "ONE"), bimap);
201    assertNull(bimap.forcePut(2, "two"));
202    assertEquals(ImmutableMap.of(-1, "ONE", 2, "two"), bimap);
203    assertEquals("two", bimap.forcePut(2, "ONE"));
204    assertEquals(ImmutableMap.of(2, "ONE"), bimap);
205  }
206
207  public void testRemove() {
208    Map<Integer, String> map = Maps.newHashMap();
209    map.put(0, null);
210    map.put(1, "one");
211    map.put(null, "null");
212
213    bimap.putAll(map);
214    assertNull(bimap.remove(0));
215
216    map.remove(0);
217    assertEquals(map, bimap);
218
219    assertEquals("null", bimap.remove(null));
220    assertEquals(Collections.singletonMap(1, "one"), bimap);
221
222    assertNull(bimap.remove(15));
223
224    assertEquals("one", bimap.remove(1));
225    assertTrue(bimap.isEmpty());
226  }
227
228  public void testSize() {
229    assertEquals(0, bimap.size());
230    bimap.put(1, "one");
231    assertEquals(1, bimap.size());
232    bimap.put(1, "ONE");
233    assertEquals(1, bimap.size());
234    bimap.put(2, "two");
235    assertEquals(2, bimap.size());
236    bimap.forcePut(1, "two");
237    assertEquals(1, bimap.size());
238  }
239
240  public void testToString() {
241    bimap.put(1, "one");
242    bimap.put(2, "two");
243
244    String string = bimap.toString();
245    String expected = string.startsWith("{1")
246        ? "{1=one, 2=two}"
247        : "{2=two, 1=one}";
248    assertEquals(expected, bimap.toString());
249  }
250
251  // Entry Set
252
253  public void testEntrySetAdd() {
254    try {
255      entrySet.add(Maps.immutableEntry(1, "one"));
256      fail();
257    } catch (UnsupportedOperationException expected) {
258    }
259  }
260
261  public void testEntrySetAddAll() {
262    try {
263      entrySet.addAll(Collections.singleton(Maps.immutableEntry(1, "one")));
264      fail();
265    } catch (UnsupportedOperationException expected) {
266    }
267  }
268
269  public void testEntrySetClear() {
270    entrySet.clear();
271    assertTrue(entrySet.isEmpty());
272    assertTrue(bimap.isEmpty());
273    putOneTwoThree();
274    entrySet.clear();
275    assertTrue(entrySet.isEmpty());
276    assertTrue(bimap.isEmpty());
277  }
278
279  public void testEntrySetContains() {
280    assertFalse(entrySet.contains(Maps.immutableEntry(1, "one")));
281    bimap.put(1, "one");
282    assertTrue(entrySet.contains(Maps.immutableEntry(1, "one")));
283    assertFalse(entrySet.contains(Maps.immutableEntry(1, "")));
284    assertFalse(entrySet.contains(Maps.immutableEntry(0, "one")));
285    assertFalse(entrySet.contains(Maps.immutableEntry(1, null)));
286    assertFalse(entrySet.contains(Maps.immutableEntry(null, "one")));
287    assertFalse(entrySet.contains(Maps.immutableEntry(null, null)));
288
289    bimap.put(null, null);
290    assertTrue(entrySet.contains(Maps.immutableEntry(1, "one")));
291    assertTrue(entrySet.contains(Maps.immutableEntry(null, null)));
292    assertFalse(entrySet.contains(Maps.immutableEntry(1, "")));
293    assertFalse(entrySet.contains(Maps.immutableEntry(0, "one")));
294    assertFalse(entrySet.contains(Maps.immutableEntry(1, null)));
295    assertFalse(entrySet.contains(Maps.immutableEntry(null, "one")));
296
297    bimap.put(null, "null");
298    bimap.put(0, null);
299    assertTrue(entrySet.contains(Maps.immutableEntry(1, "one")));
300    assertTrue(entrySet.contains(Maps.immutableEntry(null, "null")));
301    assertTrue(entrySet.contains(Maps.immutableEntry(0, null)));
302    assertFalse(entrySet.contains(Maps.immutableEntry(1, "")));
303    assertFalse(entrySet.contains(Maps.immutableEntry(0, "one")));
304    assertFalse(entrySet.contains(Maps.immutableEntry(1, null)));
305    assertFalse(entrySet.contains(Maps.immutableEntry(null, "one")));
306    assertFalse(entrySet.contains(Maps.immutableEntry(null, null)));
307  }
308
309  public void testEntrySetIsEmpty() {
310    assertTrue(entrySet.isEmpty());
311    bimap.put(1, "one");
312    assertFalse(entrySet.isEmpty());
313    bimap.remove(1);
314    assertTrue(entrySet.isEmpty());
315  }
316
317  public void testEntrySetRemove() {
318    putOneTwoThree();
319    assertTrue(bimap.containsKey(1));
320    assertTrue(bimap.containsValue("one"));
321    assertTrue(entrySet.remove(Maps.immutableEntry(1, "one")));
322    assertFalse(bimap.containsKey(1));
323    assertFalse(bimap.containsValue("one"));
324    assertEquals(2, bimap.size());
325    assertEquals(2, bimap.inverse().size());
326    assertFalse(entrySet.remove(Maps.immutableEntry(2, "three")));
327    assertFalse(entrySet.remove(3));
328    assertEquals(2, bimap.size());
329    assertEquals(2, bimap.inverse().size());
330  }
331
332  public void testEntrySetRemoveAll() {
333    putOneTwoThree();
334    assertTrue(bimap.containsKey(1));
335    assertTrue(bimap.containsValue("one"));
336    assertTrue(entrySet.removeAll(
337        Collections.singleton(Maps.immutableEntry(1, "one"))));
338    assertFalse(bimap.containsKey(1));
339    assertFalse(bimap.containsValue("one"));
340    assertEquals(2, bimap.size());
341    assertEquals(2, bimap.inverse().size());
342  }
343
344  public void testEntrySetValue() {
345    bimap.put(1, "one");
346    Entry<Integer, String> entry = bimap.entrySet().iterator().next();
347    bimap.put(2, "two");
348    assertEquals("one", entry.getValue());
349    bimap.put(1, "one");
350    assertEquals("one", entry.getValue());
351    assertEquals("one", bimap.get(1));
352    assertEquals(Integer.valueOf(1), bimap.inverse().get("one"));
353    bimap.put(1, "uno");
354    assertEquals("uno", entry.getValue());
355    assertEquals("uno", bimap.get(1));
356    assertEquals(Integer.valueOf(1), bimap.inverse().get("uno"));
357    assertEquals(2, bimap.size());
358    assertEquals(2, bimap.inverse().size());
359    try {
360      entry.setValue("two");
361      fail();
362    } catch (IllegalArgumentException expected) {}
363    assertEquals("uno", entry.getValue());
364    assertEquals("uno", bimap.get(1));
365    assertEquals(Integer.valueOf(1), bimap.inverse().get("uno"));
366    assertEquals(2, bimap.size());
367    assertEquals(2, bimap.inverse().size());
368  }
369
370  public void testEntrySetValueRemovedEntry() {
371    bimap.put(1, "a");
372    Entry<Integer, String> entry = bimap.entrySet().iterator().next();
373    bimap.clear();
374    try {
375      entry.setValue("b");
376      fail();
377    } catch (IllegalStateException expected) {}
378    assertEquals(0, bimap.size());
379    assertEquals(0, bimap.inverse().size());
380  }
381
382  public void testEntrySetValueRemovedEntryNullOldValue() {
383    bimap.put(1, null);
384    Entry<Integer, String> entry = bimap.entrySet().iterator().next();
385    bimap.clear();
386    try {
387      entry.setValue("b");
388      fail();
389    } catch (IllegalStateException expected) {}
390    assertEquals(0, bimap.size());
391    assertEquals(0, bimap.inverse().size());
392  }
393
394  public void testEntrySetValueRemovedEntryAddedEqualEntry() {
395    bimap.put(1, "a");
396    Entry<Integer, String> entry = bimap.entrySet().iterator().next();
397    bimap.clear();
398    bimap.put(1, "a");
399    try {
400      entry.setValue("b");
401      fail();
402    } catch (IllegalStateException expected) {}
403    assertEquals(1, bimap.size());
404    assertEquals("a", bimap.get(1));
405    assertEquals(1, bimap.inverse().size());
406    assertEquals((Integer) 1, bimap.inverse().get("a"));
407  }
408
409  public void testKeySetIteratorRemove() {
410    putOneTwoThree();
411    Iterator<Integer> iterator = bimap.keySet().iterator();
412    iterator.next();
413    iterator.remove();
414    assertEquals(2, bimap.size());
415    assertEquals(2, bimap.inverse().size());
416  }
417
418  public void testKeySetRemoveAll() {
419    putOneTwoThree();
420    Set<Integer> keySet = bimap.keySet();
421    assertTrue(keySet.removeAll(asList(1, 3)));
422    assertEquals(1, bimap.size());
423    assertTrue(keySet.contains(2));
424  }
425
426  public void testKeySetRetainAll() {
427    putOneTwoThree();
428    Set<Integer> keySet = bimap.keySet();
429    assertTrue(keySet.retainAll(Collections.singleton(2)));
430    assertEquals(1, bimap.size());
431    assertTrue(keySet.contains(2));
432  }
433
434  public void testEntriesIteratorRemove() {
435    putOneTwoThree();
436    Iterator<Entry<Integer, String>> iterator = bimap.entrySet().iterator();
437    iterator.next();
438    iterator.remove();
439    assertEquals(2, bimap.size());
440    assertEquals(2, bimap.inverse().size());
441  }
442
443  public void testEntriesRetainAll() {
444    putOneTwoThree();
445    Set<Map.Entry<Integer, String>> entries = bimap.entrySet();
446    Map.Entry<Integer, String> entry = Maps.immutableEntry(2, "two");
447    assertTrue(entries.retainAll(Collections.singleton(entry)));
448    assertEquals(1, bimap.size());
449    assertTrue(bimap.containsKey(2));
450  }
451
452  public void testValuesIteratorRemove() {
453    putOneTwoThree();
454    Iterator<String> iterator = bimap.values().iterator();
455    iterator.next();
456    iterator.remove();
457    assertEquals(2, bimap.size());
458    assertEquals(2, bimap.inverse().size());
459  }
460
461  public void testValuesToArray() {
462    bimap.put(1, "one");
463    String[] array = new String[3];
464    array[1] = "garbage";
465    assertSame(array, bimap.values().toArray(array));
466    assertEquals("one", array[0]);
467    assertNull(array[1]);
468  }
469
470  public void testValuesToString() {
471    bimap.put(1, "one");
472    assertEquals("[one]", bimap.values().toString());
473  }
474
475  @GwtIncompatible("SerializableTester")
476  public void testSerialization() {
477    bimap.put(1, "one");
478    bimap.put(2, "two");
479    bimap.put(3, "three");
480    bimap.put(null, null);
481
482    BiMap<Integer, String> copy =
483        SerializableTester.reserializeAndAssert(bimap);
484    assertEquals(bimap.inverse(), copy.inverse());
485  }
486
487  void putOneTwoThree() {
488    bimap.put(1, "one");
489    bimap.put(2, "two");
490    bimap.put(3, "three");
491  }
492
493  @GwtIncompatible("used only by @GwtIncompatible code")
494  private static class BiMapPair implements Serializable {
495    final BiMap<Integer, String> forward;
496    final BiMap<String, Integer> backward;
497
498    BiMapPair(BiMap<Integer, String> original) {
499      this.forward = original;
500      this.backward = original.inverse();
501    }
502
503    private static final long serialVersionUID = 0;
504  }
505
506  @GwtIncompatible("SerializableTester")
507  public void testSerializationWithInverseEqual() {
508    bimap.put(1, "one");
509    bimap.put(2, "two");
510    bimap.put(3, "three");
511    bimap.put(null, null);
512
513    BiMapPair pair = new BiMapPair(bimap);
514    BiMapPair copy = SerializableTester.reserialize(pair);
515    assertEquals(pair.forward, copy.forward);
516    assertEquals(pair.backward, copy.backward);
517
518    copy.forward.put(4, "four");
519    copy.backward.put("five", 5);
520    assertEquals(copy.backward, copy.forward.inverse());
521    assertEquals(copy.forward, copy.backward.inverse());
522
523    assertTrue(copy.forward.containsKey(4));
524    assertTrue(copy.forward.containsKey(5));
525    assertTrue(copy.backward.containsValue(4));
526    assertTrue(copy.backward.containsValue(5));
527    assertTrue(copy.forward.containsValue("four"));
528    assertTrue(copy.forward.containsValue("five"));
529    assertTrue(copy.backward.containsKey("four"));
530    assertTrue(copy.backward.containsKey("five"));
531  }
532
533  /**
534   * The sameness checks ensure that a bimap and its inverse remain consistent,
535   * even after the deserialized instances are updated. Also, the relationship
536   * {@code a == b.inverse()} should continue to hold after both bimaps are
537   * serialized and deserialized together.
538   */
539  @GwtIncompatible("SerializableTester")
540  public void testSerializationWithInverseSame() {
541    bimap.put(1, "one");
542    bimap.put(2, "two");
543    bimap.put(3, "three");
544    bimap.put(null, null);
545
546    BiMapPair pair = new BiMapPair(bimap);
547    BiMapPair copy = SerializableTester.reserialize(pair);
548    assertSame(copy.backward, copy.forward.inverse());
549    assertSame(copy.forward, copy.backward.inverse());
550  }
551}
552