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