17a6f2e1edf8d308b76e258d2363e6cb90daadae
[yangtools.git] / common / util / src / test / java / org / opendaylight / yangtools / util / OffsetMapTest.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.util;
9
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertFalse;
12 import static org.junit.Assert.assertNotSame;
13 import static org.junit.Assert.assertNull;
14 import static org.junit.Assert.assertSame;
15 import static org.junit.Assert.assertTrue;
16 import static org.junit.Assert.fail;
17 import com.google.common.collect.ImmutableMap;
18 import com.google.common.collect.Iterators;
19 import java.io.ByteArrayInputStream;
20 import java.io.ByteArrayOutputStream;
21 import java.io.IOException;
22 import java.io.ObjectInputStream;
23 import java.io.ObjectOutputStream;
24 import java.util.AbstractMap.SimpleEntry;
25 import java.util.Collections;
26 import java.util.ConcurrentModificationException;
27 import java.util.Iterator;
28 import java.util.Map;
29 import java.util.Map.Entry;
30 import java.util.NoSuchElementException;
31 import java.util.Set;
32 import org.junit.Before;
33 import org.junit.Test;
34
35 public class OffsetMapTest {
36     private final Map<String, String> twoEntryMap = ImmutableMap.of("k1", "v1", "k2", "v2");
37     private final Map<String, String> threeEntryMap = ImmutableMap.of("k1", "v1", "k2", "v2", "k3", "v3");
38
39     private ImmutableOffsetMap<String, String> createMap() {
40         return (ImmutableOffsetMap<String, String>) ImmutableOffsetMap.copyOf(twoEntryMap);
41     }
42
43     @Before
44     public void setup() {
45         OffsetMapCache.invalidateCache();
46     }
47
48     @Test(expected=IllegalArgumentException.class)
49     public void testWrongImmutableConstruction() {
50         new ImmutableOffsetMap.Ordered<String, String>(Collections.<String, Integer>emptyMap(), new String[1]);
51     }
52
53     @Test
54     public void testCopyEmptyMap() {
55         final Map<String, String> source = Collections.emptyMap();
56         final Map<String, String> result = ImmutableOffsetMap.copyOf(source);
57
58         assertEquals(source, result);
59         assertTrue(result instanceof ImmutableMap);
60     }
61
62     @Test
63     public void testCopySingletonMap() {
64         final Map<String, String> source = Collections.singletonMap("a", "b");
65         final Map<String, String> result = ImmutableOffsetMap.copyOf(source);
66
67         assertEquals(source, result);
68         assertTrue(result instanceof SharedSingletonMap);
69     }
70
71     @Test
72     public void testCopyMap() {
73         final ImmutableOffsetMap<String, String> map = createMap();
74
75         // Equality in both directions
76         assertEquals(twoEntryMap, map);
77         assertEquals(map, twoEntryMap);
78
79         // hashcode has to match
80         assertEquals(twoEntryMap.hashCode(), map.hashCode());
81
82         // Iterator order needs to be preserved
83         assertTrue(Iterators.elementsEqual(twoEntryMap.entrySet().iterator(), map.entrySet().iterator()));
84
85         // Should result in the same object
86         assertSame(map, ImmutableOffsetMap.copyOf(map));
87
88         final Map<String, String> mutable = map.toModifiableMap();
89         final Map<String, String> copy = ImmutableOffsetMap.copyOf(mutable);
90
91         assertEquals(mutable, copy);
92         assertEquals(map, copy);
93         assertNotSame(mutable, copy);
94         assertNotSame(map, copy);
95     }
96
97     @Test
98     public void testImmutableSimpleEquals() {
99         final Map<String, String> map = createMap();
100
101         assertTrue(map.equals(map));
102         assertFalse(map.equals(null));
103         assertFalse(map.equals("string"));
104     }
105
106     @Test
107     public void testImmutableGet() {
108         final Map<String, String> map = createMap();
109
110         assertEquals("v1", map.get("k1"));
111         assertEquals("v2", map.get("k2"));
112         assertNull(map.get("non-existent"));
113         assertNull(map.get(null));
114     }
115
116     @Test
117     public void testImmutableGuards() {
118         final Map<String, String> map = createMap();
119
120         try {
121             map.values().add("v1");
122             fail();
123         } catch (UnsupportedOperationException e) {
124         }
125
126         try {
127             map.values().remove("v1");
128             fail();
129         } catch (UnsupportedOperationException e) {
130         }
131
132         try {
133             map.values().clear();
134             fail();
135         } catch (UnsupportedOperationException e) {
136         }
137
138         try {
139             final Iterator<String> it = map.values().iterator();
140             it.next();
141             it.remove();
142             fail();
143         } catch (UnsupportedOperationException e) {
144         }
145
146         try {
147             map.keySet().add("k1");
148             fail();
149         } catch (UnsupportedOperationException e) {
150         }
151
152         try {
153             map.keySet().clear();
154             fail();
155         } catch (UnsupportedOperationException e) {
156         }
157
158         try {
159             map.keySet().remove("k1");
160             fail();
161         } catch (UnsupportedOperationException e) {
162         }
163
164         try {
165             final Iterator<String> it = map.keySet().iterator();
166             it.next();
167             it.remove();
168             fail();
169         } catch (UnsupportedOperationException e) {
170         }
171
172         try {
173             map.entrySet().clear();
174             fail();
175         } catch (UnsupportedOperationException e) {
176         }
177
178         try {
179             map.entrySet().add(new SimpleEntry<>("k1", "v1"));
180             fail();
181         } catch (UnsupportedOperationException e) {
182         }
183
184         try {
185             map.entrySet().remove(new SimpleEntry<>("k1", "v1"));
186             fail();
187         } catch (UnsupportedOperationException e) {
188         }
189
190         try {
191             final Iterator<Entry<String, String>> it = map.entrySet().iterator();
192             it.next();
193             it.remove();
194             fail();
195         } catch (UnsupportedOperationException e) {
196         }
197
198         try {
199             map.clear();
200             fail();
201         } catch (UnsupportedOperationException e) {
202         }
203
204         try {
205             map.put("k1", "fail");
206             fail();
207         } catch (UnsupportedOperationException e) {
208         }
209
210         try {
211             map.putAll(ImmutableMap.of("k1", "fail"));
212             fail();
213         } catch (UnsupportedOperationException e) {
214         }
215
216         try {
217             map.remove("k1");
218             fail();
219         } catch (UnsupportedOperationException e) {
220         }
221     }
222
223     @Test
224     public void testMutableGet() {
225         final Map<String, String> map = createMap().toModifiableMap();
226
227         map.put("k3", "v3");
228         assertEquals("v1", map.get("k1"));
229         assertEquals("v2", map.get("k2"));
230         assertEquals("v3", map.get("k3"));
231         assertNull(map.get("non-existent"));
232         assertNull(map.get(null));
233     }
234
235     @Test
236     public void testImmutableSize() {
237         final Map<String, String> map = createMap();
238         assertEquals(2, map.size());
239     }
240
241     @Test
242     public void testImmutableIsEmpty() {
243         final Map<String, String> map = createMap();
244         assertFalse(map.isEmpty());
245     }
246
247     @Test
248     public void testImmutableContains() {
249         final Map<String, String> map = createMap();
250         assertTrue(map.containsKey("k1"));
251         assertTrue(map.containsKey("k2"));
252         assertFalse(map.containsKey("non-existent"));
253         assertFalse(map.containsKey(null));
254         assertTrue(map.containsValue("v1"));
255         assertFalse(map.containsValue("non-existent"));
256     }
257
258     @Test
259     public void testImmutableEquals() {
260         final Map<String, String> map = createMap();
261
262         assertFalse(map.equals(threeEntryMap));
263         assertFalse(map.equals(ImmutableMap.of("k1", "v1", "k3", "v3")));
264         assertFalse(map.equals(ImmutableMap.of("k1", "v1", "k2", "different-value")));
265     }
266
267     @Test
268     public void testMutableContains() {
269         final Map<String, String> map = createMap().toModifiableMap();
270         map.put("k3", "v3");
271         assertTrue(map.containsKey("k1"));
272         assertTrue(map.containsKey("k2"));
273         assertTrue(map.containsKey("k3"));
274         assertFalse(map.containsKey("non-existent"));
275         assertFalse(map.containsKey(null));
276     }
277
278     @Test
279     public void testtoModifiableMap() {
280         final ImmutableOffsetMap<String, String> source = createMap();
281         final Map<String, String> result = source.toModifiableMap();
282
283         // The two maps should be equal, but isolated
284         assertTrue(result instanceof MutableOffsetMap);
285         assertEquals(source, result);
286         assertEquals(result, source);
287
288         // Quick test for clearing MutableOffsetMap
289         result.clear();
290         assertEquals(0, result.size());
291         assertEquals(Collections.emptyMap(), result);
292
293         // The two maps should differ now
294         assertFalse(source.equals(result));
295         assertFalse(result.equals(source));
296
297         // The source map should still equal the template
298         assertEquals(twoEntryMap, source);
299         assertEquals(source, twoEntryMap);
300     }
301
302     @Test
303     public void testReusedFields() {
304         final ImmutableOffsetMap<String, String> source = createMap();
305         final MutableOffsetMap<String, String> mutable = source.toModifiableMap();
306
307         // Should not affect the result
308         mutable.remove("non-existent");
309
310         // Resulting map should be equal, but not the same object
311         final ImmutableOffsetMap<String, String> result = (ImmutableOffsetMap<String, String>) mutable.toUnmodifiableMap();
312         assertNotSame(source, result);
313         assertEquals(source, result);
314
315         // Internal fields should be reused
316         assertSame(source.offsets(), result.offsets());
317         assertSame(source.objects(), result.objects());
318     }
319
320     @Test
321     public void testReusedOffsets() {
322         final ImmutableOffsetMap<String, String> source = createMap();
323         final MutableOffsetMap<String, String> mutable = source.toModifiableMap();
324
325         mutable.remove("k1");
326         mutable.put("k1", "v1");
327
328         final ImmutableOffsetMap<String, String> result = (ImmutableOffsetMap<String, String>) mutable.toUnmodifiableMap();
329         assertEquals(source, result);
330
331         // Only offsets should be shared
332         assertSame(source.offsets(), result.offsets());
333         assertNotSame(source.objects(), result.objects());
334
335         // Iterator order needs to be preserved
336         assertTrue(Iterators.elementsEqual(source.entrySet().iterator(), result.entrySet().iterator()));
337     }
338
339     @Test
340     public void testEmptyMutable() throws CloneNotSupportedException {
341         final MutableOffsetMap<String, String> map = MutableOffsetMap.of();
342         assertTrue(map.isEmpty());
343
344         final Map<String, String> other = map.clone();
345         assertEquals(other, map);
346         assertNotSame(other, map);
347     }
348
349     @Test
350     public void testMutableToEmpty() {
351         final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
352
353         mutable.remove("k1");
354         mutable.remove("k2");
355
356         assertTrue(mutable.isEmpty());
357         assertSame(ImmutableMap.of(), mutable.toUnmodifiableMap());
358     }
359
360     @Test
361     public void testMutableToSingleton() {
362         final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
363
364         mutable.remove("k1");
365
366         final Map<String, String> result = mutable.toUnmodifiableMap();
367
368         // Should devolve to a singleton
369         assertTrue(result instanceof ImmutableMap);
370         assertEquals(ImmutableMap.of("k2", "v2"), result);
371     }
372
373     @Test
374     public void testMutableToNewSingleton() {
375         final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
376
377         mutable.remove("k1");
378         mutable.put("k3", "v3");
379
380         final Map<String, String> result = mutable.toUnmodifiableMap();
381
382         assertTrue(result instanceof ImmutableOffsetMap);
383         assertEquals(ImmutableMap.of("k2", "v2", "k3", "v3"), result);
384     }
385
386     @Test
387     public void testMutableSize() {
388         final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
389         assertEquals(2, mutable.size());
390
391         mutable.put("k3", "v3");
392         assertEquals(3, mutable.size());
393         mutable.remove("k2");
394         assertEquals(2, mutable.size());
395         mutable.put("k1", "new-v1");
396         assertEquals(2, mutable.size());
397     }
398
399     @Test
400     public void testExpansionWithOrder() {
401         final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
402
403         mutable.remove("k1");
404         mutable.put("k3", "v3");
405         mutable.put("k1", "v1");
406
407         assertEquals(ImmutableMap.of("k3", "v3"), mutable.newKeys());
408
409         final Map<String, String> result = mutable.toUnmodifiableMap();
410
411         assertTrue(result instanceof ImmutableOffsetMap);
412         assertEquals(threeEntryMap, result);
413         assertEquals(result, threeEntryMap);
414         assertTrue(Iterators.elementsEqual(threeEntryMap.entrySet().iterator(), result.entrySet().iterator()));
415     }
416
417     @Test
418     public void testReplacedValue() {
419         final ImmutableOffsetMap<String, String> source = createMap();
420         final MutableOffsetMap<String, String> mutable = source.toModifiableMap();
421
422         mutable.put("k1", "replaced");
423
424         final ImmutableOffsetMap<String, String> result = (ImmutableOffsetMap<String, String>) mutable.toUnmodifiableMap();
425         final Map<String, String> reference = ImmutableMap.of("k1", "replaced", "k2", "v2");
426
427         assertEquals(reference, result);
428         assertEquals(result, reference);
429         assertSame(source.offsets(), result.offsets());
430         assertNotSame(source.objects(), result.objects());
431     }
432
433     @Test
434     public void testCloneableFlipping() throws CloneNotSupportedException {
435         final MutableOffsetMap<String, String> source = createMap().toModifiableMap();
436
437         // Must clone before mutation
438         assertTrue(source.needClone());
439
440         // Non-existent entry, should not clone
441         source.remove("non-existent");
442         assertTrue(source.needClone());
443
444         // Changes the array, should clone
445         source.remove("k1");
446         assertFalse(source.needClone());
447
448         // Create a clone of the map, which shares the array
449         final MutableOffsetMap<String, String> result = source.clone();
450         assertFalse(source.needClone());
451         assertTrue(result.needClone());
452         assertSame(source.array(), result.array());
453
454         // Changes the array, should clone
455         source.put("k1", "v2");
456         assertFalse(source.needClone());
457         assertTrue(result.needClone());
458
459         // Creates a immutable view, which shares the array
460         final ImmutableOffsetMap<String, String> immutable = (ImmutableOffsetMap<String, String>) source.toUnmodifiableMap();
461         assertTrue(source.needClone());
462         assertSame(source.array(), immutable.objects());
463     }
464
465     @Test
466     public void testMutableEntrySet() {
467         final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
468
469         assertTrue(map.entrySet().add(new SimpleEntry<>("k3", "v3")));
470         assertTrue(map.containsKey("k3"));
471         assertEquals("v3", map.get("k3"));
472
473         // null is not an Entry: ignore
474         assertFalse(map.entrySet().remove(null));
475
476         // non-matching value: ignore
477         assertFalse(map.entrySet().remove(new SimpleEntry<>("k1", "other")));
478         assertTrue(map.containsKey("k1"));
479
480         // ignore null values
481         assertFalse(map.entrySet().remove(new SimpleEntry<>("k1", null)));
482         assertTrue(map.containsKey("k1"));
483
484         assertTrue(map.entrySet().remove(new SimpleEntry<>("k1", "v1")));
485         assertFalse(map.containsKey("k1"));
486     }
487
488     private static void assertIteratorBroken(final Iterator<?> it) {
489         try {
490             it.hasNext();
491             fail();
492         } catch (ConcurrentModificationException e) {
493         }
494         try {
495             it.next();
496             fail();
497         } catch (ConcurrentModificationException e) {
498         }
499         try {
500             it.remove();
501             fail();
502         } catch (ConcurrentModificationException e) {
503         }
504     }
505
506     @Test
507     public void testMutableSimpleEquals() {
508         final ImmutableOffsetMap<String, String> source = createMap();
509         final Map<String, String> map = source.toModifiableMap();
510
511         assertTrue(map.equals(map));
512         assertFalse(map.equals(null));
513         assertFalse(map.equals("string"));
514         assertTrue(map.equals(source));
515     }
516
517     @Test
518     public void testMutableSimpleHashCode() {
519         final Map<String, String> map = createMap().toModifiableMap();
520
521         assertEquals(twoEntryMap.hashCode(), map.hashCode());
522     }
523
524     @Test
525     public void testMutableIteratorBasics() {
526         final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
527         final Iterator<Entry<String, String>> it = map.entrySet().iterator();
528
529         // Not advanced, remove should fail
530         try {
531             it.remove();
532             fail();
533         } catch (IllegalStateException e) {
534         }
535
536         assertTrue(it.hasNext());
537         assertEquals("k1", it.next().getKey());
538         assertTrue(it.hasNext());
539         assertEquals("k2", it.next().getKey());
540         assertFalse(it.hasNext());
541
542         // Check end-of-iteration throw
543         try {
544             it.next();
545             fail();
546         } catch (NoSuchElementException e) {
547         }
548     }
549
550     @Test
551     public void testMutableIteratorWithRemove() {
552         final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
553         final Iterator<Entry<String, String>> it = map.entrySet().iterator();
554
555         // Advance one element
556         assertTrue(it.hasNext());
557         assertEquals("k1", it.next().getKey());
558
559         // Remove k1
560         it.remove();
561         assertEquals(1, map.size());
562         assertFalse(map.containsKey("k1"));
563
564         // Iterator should still work
565         assertTrue(it.hasNext());
566         assertEquals("k2", it.next().getKey());
567         assertFalse(it.hasNext());
568     }
569
570     @Test
571     public void testMutableIteratorOffsetReplaceWorks() {
572         final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
573         final Iterator<Entry<String, String>> it = map.entrySet().iterator();
574         it.next();
575
576         map.put("k1", "new-v1");
577         assertTrue(it.hasNext());
578     }
579
580     @Test
581     public void testMutableIteratorNewReplaceWorks() {
582         final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
583         map.put("k3", "v3");
584         final Iterator<Entry<String, String>> it = map.entrySet().iterator();
585         it.next();
586
587         map.put("k3", "new-v3");
588         assertTrue(it.hasNext());
589     }
590
591     @Test
592     public void testMutableIteratorOffsetAddBreaks() {
593         final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
594         map.put("k3", "v3");
595         map.remove("k1");
596
597         final Iterator<Entry<String, String>> it = map.entrySet().iterator();
598         it.next();
599
600         map.put("k1", "new-v1");
601         assertIteratorBroken(it);
602     }
603
604     @Test
605     public void testMutableIteratorNewAddBreaks() {
606         final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
607         final Iterator<Entry<String, String>> it = map.entrySet().iterator();
608         it.next();
609
610         map.put("k3", "v3");
611         assertIteratorBroken(it);
612     }
613
614     @Test
615     public void testMutableIteratorOffsetRemoveBreaks() {
616         final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
617         final Iterator<Entry<String, String>> it = map.entrySet().iterator();
618         it.next();
619
620         map.remove("k1");
621         assertIteratorBroken(it);
622     }
623
624     @Test
625     public void testMutableIteratorNewRemoveBreaks() {
626         final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
627         map.put("k3", "v3");
628         final Iterator<Entry<String, String>> it = map.entrySet().iterator();
629         it.next();
630
631         map.remove("k3");
632         assertIteratorBroken(it);
633     }
634
635     @Test
636     public void testMutableCrossIteratorRemove() {
637         final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
638         final Set<Entry<String, String>> es = map.entrySet();
639         final Iterator<Entry<String, String>> it1 = es.iterator();
640         final Iterator<Entry<String, String>> it2 = es.iterator();
641
642         // Remove k1 via it1
643         it1.next();
644         it2.next();
645         it1.remove();
646         assertEquals(1, map.size());
647
648         // Check it2 was broken
649         assertIteratorBroken(it2);
650     }
651
652     @Test
653     public void testImmutableSerialization() throws IOException, ClassNotFoundException {
654         final Map<String, String> source = createMap();
655
656         final ByteArrayOutputStream bos = new ByteArrayOutputStream();
657         try (final ObjectOutputStream oos = new ObjectOutputStream(bos)) {
658             oos.writeObject(source);
659         }
660
661         final ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
662         @SuppressWarnings("unchecked")
663         final Map<String, String> result = (Map<String, String>) ois.readObject();
664
665         assertEquals(source, result);
666     }
667 }