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;
17 import com.google.common.collect.ImmutableMap;
18 import com.google.common.collect.ImmutableSet;
19 import com.google.common.collect.Iterators;
20 import java.io.ByteArrayInputStream;
21 import java.io.ByteArrayOutputStream;
22 import java.io.IOException;
23 import java.io.ObjectInputStream;
24 import java.io.ObjectOutputStream;
25 import java.util.AbstractMap.SimpleEntry;
26 import java.util.Collections;
27 import java.util.ConcurrentModificationException;
28 import java.util.Iterator;
30 import java.util.Map.Entry;
31 import java.util.NoSuchElementException;
33 import org.junit.Test;
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");
39 private ImmutableOffsetMap<String, String> createMap() {
40 return (ImmutableOffsetMap<String, String>) ImmutableOffsetMap.copyOf(twoEntryMap);
43 @Test(expected=IllegalArgumentException.class)
44 public void testWrongImmutableConstruction() {
45 new ImmutableOffsetMap<String, String>(Collections.<String, Integer>emptyMap(), new Object[1]);
49 public void testCopyEmptyMap() {
50 final Map<String, String> source = Collections.emptyMap();
51 final Map<String, String> result = ImmutableOffsetMap.copyOf(source);
53 assertEquals(source, result);
54 assertTrue(result instanceof ImmutableMap);
58 public void testCopySingletonMap() {
59 final Map<String, String> source = Collections.singletonMap("a", "b");
60 final Map<String, String> result = ImmutableOffsetMap.copyOf(source);
62 assertEquals(source, result);
63 assertTrue(result instanceof ImmutableMap);
67 public void testCopyMap() {
68 final ImmutableOffsetMap<String, String> map = createMap();
70 // Equality in both directions
71 assertEquals(twoEntryMap, map);
72 assertEquals(map, twoEntryMap);
74 // hashcode has to match
75 assertEquals(twoEntryMap.hashCode(), map.hashCode());
77 // Iterator order needs to be preserved
78 assertTrue(Iterators.elementsEqual(twoEntryMap.entrySet().iterator(), map.entrySet().iterator()));
80 // Should result in the same object
81 assertSame(map, ImmutableOffsetMap.copyOf(map));
83 final Map<String, String> mutable = map.toModifiableMap();
84 final Map<String, String> copy = ImmutableOffsetMap.copyOf(mutable);
86 assertEquals(mutable, copy);
87 assertEquals(map, copy);
88 assertNotSame(mutable, copy);
89 assertNotSame(map, copy);
93 public void testImmutableSimpleEquals() {
94 final Map<String, String> map = createMap();
96 assertTrue(map.equals(map));
97 assertFalse(map.equals(null));
98 assertFalse(map.equals("string"));
102 public void testImmutableCopyConstructor() {
103 final ImmutableOffsetMap<String, String> source = createMap();
104 final ImmutableOffsetMap<String, String> result = new ImmutableOffsetMap<>(source);
106 assertSame(source.offsets(), result.offsets());
107 assertSame(source.objects(), result.objects());
111 public void testImmutableGet() {
112 final Map<String, String> map = createMap();
114 assertEquals("v1", map.get("k1"));
115 assertEquals("v2", map.get("k2"));
116 assertNull(map.get("non-existent"));
117 assertNull(map.get(null));
121 public void testImmutableGuards() {
122 final Map<String, String> map = createMap();
125 map.values().add("v1");
127 } catch (UnsupportedOperationException e) {
131 map.values().remove("v1");
133 } catch (UnsupportedOperationException e) {
137 map.values().clear();
139 } catch (UnsupportedOperationException e) {
143 final Iterator<String> it = map.values().iterator();
147 } catch (UnsupportedOperationException e) {
151 map.keySet().add("k1");
153 } catch (UnsupportedOperationException e) {
157 map.keySet().clear();
159 } catch (UnsupportedOperationException e) {
163 map.keySet().remove("k1");
165 } catch (UnsupportedOperationException e) {
169 final Iterator<String> it = map.keySet().iterator();
173 } catch (UnsupportedOperationException e) {
177 map.entrySet().clear();
179 } catch (UnsupportedOperationException e) {
183 map.entrySet().add(new SimpleEntry<>("k1", "v1"));
185 } catch (UnsupportedOperationException e) {
189 map.entrySet().remove(new SimpleEntry<>("k1", "v1"));
191 } catch (UnsupportedOperationException e) {
195 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
199 } catch (UnsupportedOperationException e) {
205 } catch (UnsupportedOperationException e) {
209 map.put("k1", "fail");
211 } catch (UnsupportedOperationException e) {
215 map.putAll(ImmutableMap.of("k1", "fail"));
217 } catch (UnsupportedOperationException e) {
223 } catch (UnsupportedOperationException e) {
228 public void testMutableGet() {
229 final Map<String, String> map = createMap().toModifiableMap();
232 assertEquals("v1", map.get("k1"));
233 assertEquals("v2", map.get("k2"));
234 assertEquals("v3", map.get("k3"));
235 assertNull(map.get("non-existent"));
236 assertNull(map.get(null));
240 public void testImmutableSize() {
241 final Map<String, String> map = createMap();
242 assertEquals(2, map.size());
246 public void testImmutableIsEmpty() {
247 final Map<String, String> map = createMap();
248 assertFalse(map.isEmpty());
252 public void testImmutableContains() {
253 final Map<String, String> map = createMap();
254 assertTrue(map.containsKey("k1"));
255 assertTrue(map.containsKey("k2"));
256 assertFalse(map.containsKey("non-existent"));
257 assertFalse(map.containsKey(null));
258 assertTrue(map.containsValue("v1"));
259 assertFalse(map.containsValue("non-existent"));
263 public void testImmutableEquals() {
264 final Map<String, String> map = createMap();
266 assertFalse(map.equals(threeEntryMap));
267 assertFalse(map.equals(ImmutableMap.of("k1", "v1", "k3", "v3")));
268 assertFalse(map.equals(ImmutableMap.of("k1", "v1", "k2", "different-value")));
272 public void testMutableContains() {
273 final Map<String, String> map = createMap().toModifiableMap();
275 assertTrue(map.containsKey("k1"));
276 assertTrue(map.containsKey("k2"));
277 assertTrue(map.containsKey("k3"));
278 assertFalse(map.containsKey("non-existent"));
279 assertFalse(map.containsKey(null));
283 public void testtoModifiableMap() {
284 final ImmutableOffsetMap<String, String> source = createMap();
285 final Map<String, String> result = source.toModifiableMap();
287 // The two maps should be equal, but isolated
288 assertTrue(result instanceof MutableOffsetMap);
289 assertEquals(source, result);
290 assertEquals(result, source);
292 // Quick test for clearing MutableOffsetMap
294 assertEquals(0, result.size());
295 assertEquals(Collections.emptyMap(), result);
297 // The two maps should differ now
298 assertFalse(source.equals(result));
299 assertFalse(result.equals(source));
301 // The source map should still equal the template
302 assertEquals(twoEntryMap, source);
303 assertEquals(source, twoEntryMap);
307 public void testReusedFields() {
308 final ImmutableOffsetMap<String, String> source = createMap();
309 final MutableOffsetMap<String, String> mutable = source.toModifiableMap();
311 // Should not affect the result
312 mutable.remove("non-existent");
314 // Resulting map should be equal, but not the same object
315 final ImmutableOffsetMap<String, String> result = (ImmutableOffsetMap<String, String>) mutable.toUnmodifiableMap();
316 assertNotSame(source, result);
317 assertEquals(source, result);
319 // Internal fields should be reused
320 assertSame(source.offsets(), result.offsets());
321 assertSame(source.objects(), result.objects());
325 public void testReusedOffsets() {
326 final ImmutableOffsetMap<String, String> source = createMap();
327 final MutableOffsetMap<String, String> mutable = source.toModifiableMap();
329 mutable.remove("k1");
330 mutable.put("k1", "v1");
332 final ImmutableOffsetMap<String, String> result = (ImmutableOffsetMap<String, String>) mutable.toUnmodifiableMap();
333 assertEquals(source, result);
335 // Only offsets should be shared
336 assertSame(source.offsets(), result.offsets());
337 assertNotSame(source.objects(), result.objects());
339 // Iterator order needs to be preserved
340 assertTrue(Iterators.elementsEqual(source.entrySet().iterator(), result.entrySet().iterator()));
344 public void testEmptyMutable() throws CloneNotSupportedException {
345 final MutableOffsetMap<String, String> map = new MutableOffsetMap<>();
346 assertTrue(map.isEmpty());
348 final Map<String, String> other = map.clone();
349 assertEquals(other, map);
350 assertNotSame(other, map);
354 public void testMutableWithKeyset() {
355 final MutableOffsetMap<String, String> map = new MutableOffsetMap<>(ImmutableSet.of("k1", "k2"));
356 assertTrue(map.isEmpty());
357 assertTrue(map.keySet().isEmpty());
358 assertNull(map.get("k1"));
359 assertNull(map.remove("k2"));
363 public void testMutableToEmpty() {
364 final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
366 mutable.remove("k1");
367 mutable.remove("k2");
369 assertTrue(mutable.isEmpty());
370 assertSame(ImmutableMap.of(), mutable.toUnmodifiableMap());
374 public void testMutableToSingleton() {
375 final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
377 mutable.remove("k1");
379 final Map<String, String> result = mutable.toUnmodifiableMap();
381 // Should devolve to a singleton
382 assertTrue(result instanceof ImmutableMap);
383 assertEquals(ImmutableMap.of("k2", "v2"), result);
387 public void testMutableToNewSingleton() {
388 final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
390 mutable.remove("k1");
391 mutable.put("k3", "v3");
393 final Map<String, String> result = mutable.toUnmodifiableMap();
395 assertTrue(result instanceof ImmutableOffsetMap);
396 assertEquals(ImmutableMap.of("k2", "v2", "k3", "v3"), result);
400 public void testMutableSize() {
401 final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
402 assertEquals(2, mutable.size());
404 mutable.put("k3", "v3");
405 assertEquals(3, mutable.size());
406 mutable.remove("k2");
407 assertEquals(2, mutable.size());
408 mutable.put("k1", "new-v1");
409 assertEquals(2, mutable.size());
413 public void testExpansionWithOrder() {
414 final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
416 mutable.remove("k1");
417 mutable.put("k3", "v3");
418 mutable.put("k1", "v1");
420 assertEquals(ImmutableMap.of("k3", "v3"), mutable.newKeys());
422 final Map<String, String> result = mutable.toUnmodifiableMap();
424 assertTrue(result instanceof ImmutableOffsetMap);
425 assertEquals(threeEntryMap, result);
426 assertEquals(result, threeEntryMap);
427 assertTrue(Iterators.elementsEqual(threeEntryMap.entrySet().iterator(), result.entrySet().iterator()));
431 public void testReplacedValue() {
432 final ImmutableOffsetMap<String, String> source = createMap();
433 final MutableOffsetMap<String, String> mutable = source.toModifiableMap();
435 mutable.put("k1", "replaced");
437 final ImmutableOffsetMap<String, String> result = (ImmutableOffsetMap<String, String>) mutable.toUnmodifiableMap();
438 final Map<String, String> reference = ImmutableMap.of("k1", "replaced", "k2", "v2");
440 assertEquals(reference, result);
441 assertEquals(result, reference);
442 assertSame(source.offsets(), result.offsets());
443 assertNotSame(source.objects(), result.objects());
447 public void testCloneableFlipping() throws CloneNotSupportedException {
448 final MutableOffsetMap<String, String> source = createMap().toModifiableMap();
450 // Must clone before mutation
451 assertTrue(source.needClone());
453 // Non-existent entry, should not clone
454 source.remove("non-existent");
455 assertTrue(source.needClone());
457 // Changes the array, should clone
459 assertFalse(source.needClone());
461 // Create a clone of the map, which shares the array
462 final MutableOffsetMap<String, String> result = source.clone();
463 assertFalse(source.needClone());
464 assertTrue(result.needClone());
465 assertSame(source.array(), result.array());
467 // Changes the array, should clone
468 source.put("k1", "v2");
469 assertFalse(source.needClone());
470 assertTrue(result.needClone());
472 // Creates a immutable view, which shares the array
473 final ImmutableOffsetMap<String, String> immutable = (ImmutableOffsetMap<String, String>) source.toUnmodifiableMap();
474 assertTrue(source.needClone());
475 assertSame(source.array(), immutable.objects());
479 public void testMutableEntrySet() {
480 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
482 assertTrue(map.entrySet().add(new SimpleEntry<>("k3", "v3")));
483 assertTrue(map.containsKey("k3"));
484 assertEquals("v3", map.get("k3"));
486 // null is not an Entry: ignore
487 assertFalse(map.entrySet().remove(null));
489 // non-matching value: ignore
490 assertFalse(map.entrySet().remove(new SimpleEntry<>("k1", "other")));
491 assertTrue(map.containsKey("k1"));
493 // ignore null values
494 assertFalse(map.entrySet().remove(new SimpleEntry<>("k1", null)));
495 assertTrue(map.containsKey("k1"));
497 assertTrue(map.entrySet().remove(new SimpleEntry<>("k1", "v1")));
498 assertFalse(map.containsKey("k1"));
501 private static void assertIteratorBroken(final Iterator<?> it) {
505 } catch (ConcurrentModificationException e) {
510 } catch (ConcurrentModificationException e) {
515 } catch (ConcurrentModificationException e) {
520 public void testMutableSimpleEquals() {
521 final ImmutableOffsetMap<String, String> source = createMap();
522 final Map<String, String> map = source.toModifiableMap();
524 assertTrue(map.equals(map));
525 assertFalse(map.equals(null));
526 assertFalse(map.equals("string"));
527 assertTrue(map.equals(source));
531 public void testMutableSimpleHashCode() {
532 final Map<String, String> map = createMap().toModifiableMap();
534 assertEquals(twoEntryMap.hashCode(), map.hashCode());
538 public void testMutableIteratorBasics() {
539 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
540 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
542 // Not advanced, remove should fail
546 } catch (IllegalStateException e) {
549 assertTrue(it.hasNext());
550 assertEquals("k1", it.next().getKey());
551 assertTrue(it.hasNext());
552 assertEquals("k2", it.next().getKey());
553 assertFalse(it.hasNext());
555 // Check end-of-iteration throw
559 } catch (NoSuchElementException e) {
564 public void testMutableIteratorWithRemove() {
565 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
566 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
568 // Advance one element
569 assertTrue(it.hasNext());
570 assertEquals("k1", it.next().getKey());
574 assertEquals(1, map.size());
575 assertFalse(map.containsKey("k1"));
577 // Iterator should still work
578 assertTrue(it.hasNext());
579 assertEquals("k2", it.next().getKey());
580 assertFalse(it.hasNext());
584 public void testMutableIteratorOffsetReplaceWorks() {
585 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
586 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
589 map.put("k1", "new-v1");
590 assertTrue(it.hasNext());
594 public void testMutableIteratorNewReplaceWorks() {
595 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
597 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
600 map.put("k3", "new-v3");
601 assertTrue(it.hasNext());
605 public void testMutableIteratorOffsetAddBreaks() {
606 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
610 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
613 map.put("k1", "new-v1");
614 assertIteratorBroken(it);
618 public void testMutableIteratorNewAddBreaks() {
619 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
620 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
624 assertIteratorBroken(it);
628 public void testMutableIteratorOffsetRemoveBreaks() {
629 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
630 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
634 assertIteratorBroken(it);
638 public void testMutableIteratorNewRemoveBreaks() {
639 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
641 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
645 assertIteratorBroken(it);
649 public void testMutableCrossIteratorRemove() {
650 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
651 final Set<Entry<String, String>> es = map.entrySet();
652 final Iterator<Entry<String, String>> it1 = es.iterator();
653 final Iterator<Entry<String, String>> it2 = es.iterator();
659 assertEquals(1, map.size());
661 // Check it2 was broken
662 assertIteratorBroken(it2);
666 public void testImmutableSerialization() throws IOException, ClassNotFoundException {
667 final Map<String, String> source = createMap();
669 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
670 try (final ObjectOutputStream oos = new ObjectOutputStream(bos)) {
671 oos.writeObject(source);
674 final ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
675 @SuppressWarnings("unchecked")
676 final Map<String, String> result = (Map<String, String>) ois.readObject();
678 assertEquals(source, result);