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