2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.util;
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;
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.Collections;
28 import java.util.ConcurrentModificationException;
29 import java.util.Iterator;
31 import java.util.Map.Entry;
32 import java.util.NoSuchElementException;
34 import org.junit.Before;
35 import org.junit.Test;
37 public class OffsetMapTest {
38 private final Map<String, String> twoEntryMap = ImmutableMap.of("k1", "v1", "k2", "v2");
39 private final Map<String, String> threeEntryMap = ImmutableMap.of("k1", "v1", "k2", "v2", "k3", "v3");
41 private ImmutableOffsetMap<String, String> createMap() {
42 return (ImmutableOffsetMap<String, String>) ImmutableOffsetMap.orderedCopyOf(twoEntryMap);
45 private ImmutableOffsetMap<String, String> unorderedMap() {
46 return (ImmutableOffsetMap<String, String>) ImmutableOffsetMap.unorderedCopyOf(twoEntryMap);
51 OffsetMapCache.invalidateCache();
54 @Test(expected = IllegalArgumentException.class)
55 public void testWrongImmutableConstruction() {
56 new ImmutableOffsetMap.Ordered<>(ImmutableMap.of(), new String[1]);
60 public void testCopyEmptyMap() {
61 final Map<String, String> source = Collections.emptyMap();
62 final Map<String, String> result = ImmutableOffsetMap.orderedCopyOf(source);
64 assertEquals(source, result);
65 assertTrue(result instanceof ImmutableMap);
69 public void testCopySingletonMap() {
70 final Map<String, String> source = Collections.singletonMap("a", "b");
71 final Map<String, String> result = ImmutableOffsetMap.orderedCopyOf(source);
73 assertEquals(source, result);
74 assertTrue(result instanceof SharedSingletonMap);
78 public void testCopyMap() {
79 final ImmutableOffsetMap<String, String> map = createMap();
81 // Equality in both directions
82 assertEquals(twoEntryMap, map);
83 assertEquals(map, twoEntryMap);
85 // hashcode has to match
86 assertEquals(twoEntryMap.hashCode(), map.hashCode());
88 // Iterator order needs to be preserved
89 assertTrue(Iterators.elementsEqual(twoEntryMap.entrySet().iterator(), map.entrySet().iterator()));
91 // Should result in the same object
92 assertSame(map, ImmutableOffsetMap.orderedCopyOf(map));
94 final Map<String, String> mutable = map.toModifiableMap();
95 final Map<String, String> copy = ImmutableOffsetMap.orderedCopyOf(mutable);
97 assertEquals(mutable, copy);
98 assertEquals(map, copy);
99 assertNotSame(mutable, copy);
100 assertNotSame(map, copy);
104 public void testImmutableSimpleEquals() {
105 final Map<String, String> map = createMap();
107 assertTrue(map.equals(map));
108 assertFalse(map.equals(null));
109 assertFalse(map.equals("string"));
113 public void testImmutableGet() {
114 final Map<String, String> map = createMap();
116 assertEquals("v1", map.get("k1"));
117 assertEquals("v2", map.get("k2"));
118 assertNull(map.get("non-existent"));
119 assertNull(map.get(null));
123 public void testImmutableGuards() {
124 final Map<String, String> map = createMap();
127 map.values().add("v1");
129 } catch (UnsupportedOperationException e) {
134 map.values().remove("v1");
136 } catch (UnsupportedOperationException e) {
141 map.values().clear();
143 } catch (UnsupportedOperationException e) {
148 final Iterator<String> it = map.values().iterator();
152 } catch (UnsupportedOperationException e) {
157 map.keySet().add("k1");
159 } catch (UnsupportedOperationException e) {
164 map.keySet().clear();
166 } catch (UnsupportedOperationException e) {
171 map.keySet().remove("k1");
173 } catch (UnsupportedOperationException e) {
178 final Iterator<String> it = map.keySet().iterator();
182 } catch (UnsupportedOperationException e) {
187 map.entrySet().clear();
189 } catch (UnsupportedOperationException e) {
194 map.entrySet().add(new SimpleEntry<>("k1", "v1"));
196 } catch (UnsupportedOperationException e) {
201 map.entrySet().remove(new SimpleEntry<>("k1", "v1"));
203 } catch (UnsupportedOperationException e) {
208 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
212 } catch (UnsupportedOperationException e) {
219 } catch (UnsupportedOperationException e) {
224 map.put("k1", "fail");
226 } catch (UnsupportedOperationException e) {
231 map.putAll(ImmutableMap.of("k1", "fail"));
233 } catch (UnsupportedOperationException e) {
240 } catch (UnsupportedOperationException e) {
246 public void testMutableGet() {
247 final Map<String, String> map = createMap().toModifiableMap();
250 assertEquals("v1", map.get("k1"));
251 assertEquals("v2", map.get("k2"));
252 assertEquals("v3", map.get("k3"));
253 assertNull(map.get("non-existent"));
254 assertNull(map.get(null));
258 public void testImmutableSize() {
259 final Map<String, String> map = createMap();
260 assertEquals(2, map.size());
264 public void testImmutableIsEmpty() {
265 final Map<String, String> map = createMap();
266 assertFalse(map.isEmpty());
270 public void testImmutableContains() {
271 final Map<String, String> map = createMap();
272 assertTrue(map.containsKey("k1"));
273 assertTrue(map.containsKey("k2"));
274 assertFalse(map.containsKey("non-existent"));
275 assertFalse(map.containsKey(null));
276 assertTrue(map.containsValue("v1"));
277 assertFalse(map.containsValue("non-existent"));
281 public void testImmutableEquals() {
282 final Map<String, String> map = createMap();
284 assertFalse(map.equals(threeEntryMap));
285 assertFalse(map.equals(ImmutableMap.of("k1", "v1", "k3", "v3")));
286 assertFalse(map.equals(ImmutableMap.of("k1", "v1", "k2", "different-value")));
290 public void testMutableContains() {
291 final Map<String, String> map = createMap().toModifiableMap();
293 assertTrue(map.containsKey("k1"));
294 assertTrue(map.containsKey("k2"));
295 assertTrue(map.containsKey("k3"));
296 assertFalse(map.containsKey("non-existent"));
297 assertFalse(map.containsKey(null));
301 public void testtoModifiableMap() {
302 final ImmutableOffsetMap<String, String> source = createMap();
303 final Map<String, String> result = source.toModifiableMap();
305 // The two maps should be equal, but isolated
306 assertTrue(result instanceof MutableOffsetMap);
307 assertEquals(source, result);
308 assertEquals(result, source);
310 // Quick test for clearing MutableOffsetMap
312 assertEquals(0, result.size());
313 assertEquals(Collections.emptyMap(), result);
315 // The two maps should differ now
316 assertFalse(source.equals(result));
317 assertFalse(result.equals(source));
319 // The source map should still equal the template
320 assertEquals(twoEntryMap, source);
321 assertEquals(source, twoEntryMap);
325 public void testReusedFields() {
326 final ImmutableOffsetMap<String, String> source = createMap();
327 final MutableOffsetMap<String, String> mutable = source.toModifiableMap();
329 // Should not affect the result
330 mutable.remove("non-existent");
332 // Resulting map should be equal, but not the same object
333 final ImmutableOffsetMap<String, String> result = (ImmutableOffsetMap<String, String>) mutable
334 .toUnmodifiableMap();
335 assertNotSame(source, result);
336 assertEquals(source, result);
338 // Internal fields should be reused
339 assertSame(source.offsets(), result.offsets());
340 assertSame(source.objects(), result.objects());
344 public void testReusedOffsets() {
345 final ImmutableOffsetMap<String, String> source = createMap();
346 final MutableOffsetMap<String, String> mutable = source.toModifiableMap();
348 mutable.remove("k1");
349 mutable.put("k1", "v1");
351 final ImmutableOffsetMap<String, String> result = (ImmutableOffsetMap<String, String>) mutable
352 .toUnmodifiableMap();
353 assertTrue(source.equals(result));
354 assertTrue(result.equals(source));
356 // Iterator order must not be preserved
357 assertFalse(Iterators.elementsEqual(source.entrySet().iterator(), result.entrySet().iterator()));
361 public void testReusedOffsetsUnordered() {
362 final ImmutableOffsetMap<String, String> source = unorderedMap();
363 final MutableOffsetMap<String, String> mutable = source.toModifiableMap();
365 mutable.remove("k1");
366 mutable.put("k1", "v1");
368 final ImmutableOffsetMap<String, String> result = (ImmutableOffsetMap<String, String>) mutable
369 .toUnmodifiableMap();
370 assertEquals(source, result);
372 // Only offsets should be shared
373 assertSame(source.offsets(), result.offsets());
374 assertNotSame(source.objects(), result.objects());
376 // Iterator order needs to be preserved
377 assertTrue(Iterators.elementsEqual(source.entrySet().iterator(), result.entrySet().iterator()));
381 public void testEmptyMutable() throws CloneNotSupportedException {
382 final MutableOffsetMap<String, String> map = MutableOffsetMap.ordered();
383 assertTrue(map.isEmpty());
385 final Map<String, String> other = map.clone();
386 assertEquals(other, map);
387 assertNotSame(other, map);
391 public void testMutableToEmpty() {
392 final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
394 mutable.remove("k1");
395 mutable.remove("k2");
397 assertTrue(mutable.isEmpty());
398 assertSame(ImmutableMap.of(), mutable.toUnmodifiableMap());
402 public void testMutableToSingleton() {
403 final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
405 mutable.remove("k1");
407 final Map<String, String> result = mutable.toUnmodifiableMap();
409 // Should devolve to a singleton
410 assertTrue(result instanceof SharedSingletonMap);
411 assertEquals(ImmutableMap.of("k2", "v2"), result);
415 public void testMutableToNewSingleton() {
416 final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
418 mutable.remove("k1");
419 mutable.put("k3", "v3");
421 final Map<String, String> result = mutable.toUnmodifiableMap();
423 assertTrue(result instanceof ImmutableOffsetMap);
424 assertEquals(ImmutableMap.of("k2", "v2", "k3", "v3"), result);
428 public void testMutableSize() {
429 final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
430 assertEquals(2, mutable.size());
432 mutable.put("k3", "v3");
433 assertEquals(3, mutable.size());
434 mutable.remove("k2");
435 assertEquals(2, mutable.size());
436 mutable.put("k1", "new-v1");
437 assertEquals(2, mutable.size());
441 public void testExpansionWithOrder() {
442 final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
444 mutable.remove("k1");
445 mutable.put("k3", "v3");
446 mutable.put("k1", "v1");
448 assertEquals(ImmutableMap.of("k1", "v1", "k3", "v3"), mutable.newKeys());
450 final Map<String, String> result = mutable.toUnmodifiableMap();
452 assertTrue(result instanceof ImmutableOffsetMap);
453 assertEquals(threeEntryMap, result);
454 assertEquals(result, threeEntryMap);
455 assertFalse(Iterators.elementsEqual(threeEntryMap.entrySet().iterator(), result.entrySet().iterator()));
459 public void testExpansionWithoutOrder() {
460 final MutableOffsetMap<String, String> mutable = unorderedMap().toModifiableMap();
462 mutable.remove("k1");
463 mutable.put("k3", "v3");
464 mutable.put("k1", "v1");
466 assertEquals(ImmutableMap.of("k3", "v3"), mutable.newKeys());
468 final Map<String, String> result = mutable.toUnmodifiableMap();
470 assertTrue(result instanceof ImmutableOffsetMap);
471 assertEquals(threeEntryMap, result);
472 assertEquals(result, threeEntryMap);
473 assertTrue(Iterators.elementsEqual(threeEntryMap.entrySet().iterator(), result.entrySet().iterator()));
477 public void testReplacedValue() {
478 final ImmutableOffsetMap<String, String> source = createMap();
479 final MutableOffsetMap<String, String> mutable = source.toModifiableMap();
481 mutable.put("k1", "replaced");
483 final ImmutableOffsetMap<String, String> result = (ImmutableOffsetMap<String, String>) mutable
484 .toUnmodifiableMap();
485 final Map<String, String> reference = ImmutableMap.of("k1", "replaced", "k2", "v2");
487 assertEquals(reference, result);
488 assertEquals(result, reference);
489 assertSame(source.offsets(), result.offsets());
490 assertNotSame(source.objects(), result.objects());
494 public void testCloneableFlipping() throws CloneNotSupportedException {
495 final MutableOffsetMap<String, String> source = createMap().toModifiableMap();
497 // Must clone before mutation
498 assertTrue(source.needClone());
500 // Non-existent entry, should not clone
501 source.remove("non-existent");
502 assertTrue(source.needClone());
504 // Changes the array, should clone
506 assertFalse(source.needClone());
508 // Create a clone of the map, which shares the array
509 final MutableOffsetMap<String, String> result = source.clone();
510 assertFalse(source.needClone());
511 assertTrue(result.needClone());
512 assertSame(source.array(), result.array());
514 // Changes the array, should clone
515 source.put("k1", "v2");
516 assertFalse(source.needClone());
517 assertTrue(result.needClone());
519 // Forced copy, no cloning needed, but maps are equal
520 final ImmutableOffsetMap<String, String> immutable = (ImmutableOffsetMap<String, String>) source
521 .toUnmodifiableMap();
522 assertFalse(source.needClone());
523 assertTrue(source.equals(immutable));
524 assertTrue(immutable.equals(source));
525 assertTrue(Iterables.elementsEqual(source.entrySet(), immutable.entrySet()));
529 public void testCloneableFlippingUnordered() throws CloneNotSupportedException {
530 final MutableOffsetMap<String, String> source = unorderedMap().toModifiableMap();
532 // Must clone before mutation
533 assertTrue(source.needClone());
535 // Non-existent entry, should not clone
536 source.remove("non-existent");
537 assertTrue(source.needClone());
539 // Changes the array, should clone
541 assertFalse(source.needClone());
543 // Create a clone of the map, which shares the array
544 final MutableOffsetMap<String, String> result = source.clone();
545 assertFalse(source.needClone());
546 assertTrue(result.needClone());
547 assertSame(source.array(), result.array());
549 // Changes the array, should clone
550 source.put("k1", "v2");
551 assertFalse(source.needClone());
552 assertTrue(result.needClone());
554 // Creates a immutable view, which shares the array
555 final ImmutableOffsetMap<String, String> immutable = (ImmutableOffsetMap<String, String>) source
556 .toUnmodifiableMap();
557 assertTrue(source.needClone());
558 assertSame(source.array(), immutable.objects());
562 public void testMutableEntrySet() {
563 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
565 assertTrue(map.entrySet().add(new SimpleEntry<>("k3", "v3")));
566 assertTrue(map.containsKey("k3"));
567 assertEquals("v3", map.get("k3"));
569 // null is not an Entry: ignore
570 assertFalse(map.entrySet().remove(null));
572 // non-matching value: ignore
573 assertFalse(map.entrySet().remove(new SimpleEntry<>("k1", "other")));
574 assertTrue(map.containsKey("k1"));
576 // ignore null values
577 assertFalse(map.entrySet().remove(new SimpleEntry<>("k1", null)));
578 assertTrue(map.containsKey("k1"));
580 assertTrue(map.entrySet().remove(new SimpleEntry<>("k1", "v1")));
581 assertFalse(map.containsKey("k1"));
584 private static void assertIteratorBroken(final Iterator<?> it) {
588 } catch (ConcurrentModificationException e) {
594 } catch (ConcurrentModificationException e) {
600 } catch (ConcurrentModificationException e) {
606 public void testMutableSimpleEquals() {
607 final ImmutableOffsetMap<String, String> source = createMap();
608 final Map<String, String> map = source.toModifiableMap();
610 assertTrue(map.equals(map));
611 assertFalse(map.equals(null));
612 assertFalse(map.equals("string"));
613 assertTrue(map.equals(source));
617 public void testMutableSimpleHashCode() {
618 final Map<String, String> map = createMap().toModifiableMap();
620 assertEquals(twoEntryMap.hashCode(), map.hashCode());
624 public void testMutableIteratorBasics() {
625 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
626 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
628 // Not advanced, remove should fail
632 } catch (IllegalStateException e) {
636 assertTrue(it.hasNext());
637 assertEquals("k1", it.next().getKey());
638 assertTrue(it.hasNext());
639 assertEquals("k2", it.next().getKey());
640 assertFalse(it.hasNext());
642 // Check end-of-iteration throw
646 } catch (NoSuchElementException e) {
652 public void testMutableIteratorWithRemove() {
653 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
654 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
656 // Advance one element
657 assertTrue(it.hasNext());
658 assertEquals("k1", it.next().getKey());
662 assertEquals(1, map.size());
663 assertFalse(map.containsKey("k1"));
665 // Iterator should still work
666 assertTrue(it.hasNext());
667 assertEquals("k2", it.next().getKey());
668 assertFalse(it.hasNext());
672 public void testMutableIteratorOffsetReplaceWorks() {
673 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
674 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
677 map.put("k1", "new-v1");
678 assertTrue(it.hasNext());
682 public void testMutableIteratorNewReplaceWorks() {
683 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
685 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
688 map.put("k3", "new-v3");
689 assertTrue(it.hasNext());
693 public void testMutableIteratorOffsetAddBreaks() {
694 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
698 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
701 map.put("k1", "new-v1");
702 assertIteratorBroken(it);
706 public void testMutableIteratorNewAddBreaks() {
707 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
708 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
712 assertIteratorBroken(it);
716 public void testMutableIteratorOffsetRemoveBreaks() {
717 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
718 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
722 assertIteratorBroken(it);
726 public void testMutableIteratorNewRemoveBreaks() {
727 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
729 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
733 assertIteratorBroken(it);
737 public void testMutableCrossIteratorRemove() {
738 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
739 final Set<Entry<String, String>> es = map.entrySet();
740 final Iterator<Entry<String, String>> it1 = es.iterator();
741 final Iterator<Entry<String, String>> it2 = es.iterator();
747 assertEquals(1, map.size());
749 // Check it2 was broken
750 assertIteratorBroken(it2);
754 public void testImmutableSerialization() throws IOException, ClassNotFoundException {
755 final Map<String, String> source = createMap();
757 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
758 try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {
759 oos.writeObject(source);
762 final ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
763 @SuppressWarnings("unchecked")
764 final Map<String, String> result = (Map<String, String>) ois.readObject();
766 assertEquals(source, result);