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.assertThrows;
16 import static org.junit.Assert.assertTrue;
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;
32 import java.util.Map.Entry;
33 import java.util.NoSuchElementException;
35 import org.junit.Before;
36 import org.junit.Test;
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");
42 private ImmutableOffsetMap<String, String> createMap() {
43 return (ImmutableOffsetMap<String, String>) ImmutableOffsetMap.orderedCopyOf(twoEntryMap);
46 private ImmutableOffsetMap<String, String> unorderedMap() {
47 return (ImmutableOffsetMap<String, String>) ImmutableOffsetMap.unorderedCopyOf(twoEntryMap);
52 OffsetMapCache.invalidateCache();
55 public void testWrongImmutableConstruction() {
56 assertThrows(IllegalArgumentException.class,
57 () -> new ImmutableOffsetMap.Ordered<>(ImmutableMap.of(), new String[1]));
61 public void testCopyEmptyMap() {
62 final Map<String, String> source = Collections.emptyMap();
63 final Map<String, String> result = ImmutableOffsetMap.orderedCopyOf(source);
65 assertEquals(source, result);
66 assertTrue(result instanceof ImmutableMap);
70 public void testCopySingletonMap() {
71 final Map<String, String> source = Collections.singletonMap("a", "b");
72 final Map<String, String> result = ImmutableOffsetMap.orderedCopyOf(source);
74 assertEquals(source, result);
75 assertTrue(result instanceof SharedSingletonMap);
79 public void testCopyMap() {
80 final ImmutableOffsetMap<String, String> map = createMap();
82 // Equality in both directions
83 assertEquals(twoEntryMap, map);
84 assertEquals(map, twoEntryMap);
86 // hashcode has to match
87 assertEquals(twoEntryMap.hashCode(), map.hashCode());
89 // Iterator order needs to be preserved
90 assertTrue(Iterators.elementsEqual(twoEntryMap.entrySet().iterator(), map.entrySet().iterator()));
92 // Should result in the same object
93 assertSame(map, ImmutableOffsetMap.orderedCopyOf(map));
95 final Map<String, String> mutable = map.toModifiableMap();
96 final Map<String, String> copy = ImmutableOffsetMap.orderedCopyOf(mutable);
98 assertEquals(mutable, copy);
99 assertEquals(map, copy);
100 assertNotSame(mutable, copy);
101 assertNotSame(map, copy);
105 public void testImmutableSimpleEquals() {
106 final Map<String, String> map = createMap();
108 assertTrue(map.equals(map));
109 assertFalse(map.equals(null));
110 assertFalse(map.equals("string"));
114 public void testImmutableGet() {
115 final Map<String, String> map = createMap();
117 assertEquals("v1", map.get("k1"));
118 assertEquals("v2", map.get("k2"));
119 assertNull(map.get("non-existent"));
120 assertNull(map.get(null));
124 public void testImmutableGuards() {
125 final Map<String, String> map = createMap();
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());
132 final Iterator<String> vit = values.iterator();
134 assertThrows(UnsupportedOperationException.class, () -> vit.remove());
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"));
141 final Iterator<String> kit = keySet.iterator();
143 assertThrows(UnsupportedOperationException.class, () -> kit.remove());
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")));
150 final Iterator<Entry<String, String>> eit = entrySet.iterator();
152 assertThrows(UnsupportedOperationException.class, () -> eit.remove());
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"));
161 public void testMutableGet() {
162 final Map<String, String> map = createMap().toModifiableMap();
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));
173 public void testImmutableSize() {
174 final Map<String, String> map = createMap();
175 assertEquals(2, map.size());
179 public void testImmutableIsEmpty() {
180 final Map<String, String> map = createMap();
181 assertFalse(map.isEmpty());
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"));
196 public void testImmutableEquals() {
197 final Map<String, String> map = createMap();
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")));
205 public void testMutableContains() {
206 final Map<String, String> map = createMap().toModifiableMap();
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));
216 public void testtoModifiableMap() {
217 final ImmutableOffsetMap<String, String> source = createMap();
218 final Map<String, String> result = source.toModifiableMap();
220 // The two maps should be equal, but isolated
221 assertTrue(result instanceof MutableOffsetMap);
222 assertEquals(source, result);
223 assertEquals(result, source);
225 // Quick test for clearing MutableOffsetMap
227 assertEquals(0, result.size());
228 assertEquals(Collections.emptyMap(), result);
230 // The two maps should differ now
231 assertFalse(source.equals(result));
232 assertFalse(result.equals(source));
234 // The source map should still equal the template
235 assertEquals(twoEntryMap, source);
236 assertEquals(source, twoEntryMap);
240 public void testReusedFields() {
241 final ImmutableOffsetMap<String, String> source = createMap();
242 final MutableOffsetMap<String, String> mutable = source.toModifiableMap();
244 // Should not affect the result
245 mutable.remove("non-existent");
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);
253 // Internal fields should be reused
254 assertSame(source.offsets(), result.offsets());
255 assertSame(source.objects(), result.objects());
259 public void testReusedOffsets() {
260 final ImmutableOffsetMap<String, String> source = createMap();
261 final MutableOffsetMap<String, String> mutable = source.toModifiableMap();
263 mutable.remove("k1");
264 mutable.put("k1", "v1");
266 final ImmutableOffsetMap<String, String> result = (ImmutableOffsetMap<String, String>) mutable
267 .toUnmodifiableMap();
268 assertTrue(source.equals(result));
269 assertTrue(result.equals(source));
271 // Iterator order must not be preserved
272 assertFalse(Iterators.elementsEqual(source.entrySet().iterator(), result.entrySet().iterator()));
276 public void testReusedOffsetsUnordered() {
277 final ImmutableOffsetMap<String, String> source = unorderedMap();
278 final MutableOffsetMap<String, String> mutable = source.toModifiableMap();
280 mutable.remove("k1");
281 mutable.put("k1", "v1");
283 final ImmutableOffsetMap<String, String> result = (ImmutableOffsetMap<String, String>) mutable
284 .toUnmodifiableMap();
285 assertEquals(source, result);
287 // Only offsets should be shared
288 assertSame(source.offsets(), result.offsets());
289 assertNotSame(source.objects(), result.objects());
291 // Iterator order needs to be preserved
292 assertTrue(Iterators.elementsEqual(source.entrySet().iterator(), result.entrySet().iterator()));
296 public void testEmptyMutable() throws CloneNotSupportedException {
297 final MutableOffsetMap<String, String> map = MutableOffsetMap.ordered();
298 assertTrue(map.isEmpty());
300 final Map<String, String> other = map.clone();
301 assertEquals(other, map);
302 assertNotSame(other, map);
306 public void testMutableToEmpty() {
307 final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
309 mutable.remove("k1");
310 mutable.remove("k2");
312 assertTrue(mutable.isEmpty());
313 assertSame(ImmutableMap.of(), mutable.toUnmodifiableMap());
317 public void testMutableToSingleton() {
318 final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
320 mutable.remove("k1");
322 final Map<String, String> result = mutable.toUnmodifiableMap();
324 // Should devolve to a singleton
325 assertTrue(result instanceof SharedSingletonMap);
326 assertEquals(ImmutableMap.of("k2", "v2"), result);
330 public void testMutableToNewSingleton() {
331 final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
333 mutable.remove("k1");
334 mutable.put("k3", "v3");
336 final Map<String, String> result = mutable.toUnmodifiableMap();
338 assertTrue(result instanceof ImmutableOffsetMap);
339 assertEquals(ImmutableMap.of("k2", "v2", "k3", "v3"), result);
343 public void testMutableSize() {
344 final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
345 assertEquals(2, mutable.size());
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());
356 public void testExpansionWithOrder() {
357 final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
359 mutable.remove("k1");
360 mutable.put("k3", "v3");
361 mutable.put("k1", "v1");
363 assertEquals(ImmutableMap.of("k1", "v1", "k3", "v3"), mutable.newKeys());
365 final Map<String, String> result = mutable.toUnmodifiableMap();
367 assertTrue(result instanceof ImmutableOffsetMap);
368 assertEquals(threeEntryMap, result);
369 assertEquals(result, threeEntryMap);
370 assertFalse(Iterators.elementsEqual(threeEntryMap.entrySet().iterator(), result.entrySet().iterator()));
374 public void testExpansionWithoutOrder() {
375 final MutableOffsetMap<String, String> mutable = unorderedMap().toModifiableMap();
377 mutable.remove("k1");
378 mutable.put("k3", "v3");
379 mutable.put("k1", "v1");
381 assertEquals(ImmutableMap.of("k3", "v3"), mutable.newKeys());
383 final Map<String, String> result = mutable.toUnmodifiableMap();
385 assertTrue(result instanceof ImmutableOffsetMap);
386 assertEquals(threeEntryMap, result);
387 assertEquals(result, threeEntryMap);
388 assertTrue(Iterators.elementsEqual(threeEntryMap.entrySet().iterator(), result.entrySet().iterator()));
392 public void testReplacedValue() {
393 final ImmutableOffsetMap<String, String> source = createMap();
394 final MutableOffsetMap<String, String> mutable = source.toModifiableMap();
396 mutable.put("k1", "replaced");
398 final ImmutableOffsetMap<String, String> result = (ImmutableOffsetMap<String, String>) mutable
399 .toUnmodifiableMap();
400 final Map<String, String> reference = ImmutableMap.of("k1", "replaced", "k2", "v2");
402 assertEquals(reference, result);
403 assertEquals(result, reference);
404 assertSame(source.offsets(), result.offsets());
405 assertNotSame(source.objects(), result.objects());
409 public void testCloneableFlipping() throws CloneNotSupportedException {
410 final MutableOffsetMap<String, String> source = createMap().toModifiableMap();
412 // Must clone before mutation
413 assertTrue(source.needClone());
415 // Non-existent entry, should not clone
416 source.remove("non-existent");
417 assertTrue(source.needClone());
419 // Changes the array, should clone
421 assertFalse(source.needClone());
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());
429 // Changes the array, should clone
430 source.put("k1", "v2");
431 assertFalse(source.needClone());
432 assertTrue(result.needClone());
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()));
444 public void testCloneableFlippingUnordered() throws CloneNotSupportedException {
445 final MutableOffsetMap<String, String> source = unorderedMap().toModifiableMap();
447 // Must clone before mutation
448 assertTrue(source.needClone());
450 // Non-existent entry, should not clone
451 source.remove("non-existent");
452 assertTrue(source.needClone());
454 // Changes the array, should clone
456 assertFalse(source.needClone());
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());
464 // Changes the array, should clone
465 source.put("k1", "v2");
466 assertFalse(source.needClone());
467 assertTrue(result.needClone());
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());
477 public void testMutableEntrySet() {
478 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
480 assertTrue(map.entrySet().add(new SimpleEntry<>("k3", "v3")));
481 assertTrue(map.containsKey("k3"));
482 assertEquals("v3", map.get("k3"));
484 // null is not an Entry: ignore
485 assertFalse(map.entrySet().remove(null));
487 // non-matching value: ignore
488 assertFalse(map.entrySet().remove(new SimpleEntry<>("k1", "other")));
489 assertTrue(map.containsKey("k1"));
491 // ignore null values
492 assertFalse(map.entrySet().remove(new SimpleEntry<>("k1", null)));
493 assertTrue(map.containsKey("k1"));
495 assertTrue(map.entrySet().remove(new SimpleEntry<>("k1", "v1")));
496 assertFalse(map.containsKey("k1"));
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());
506 public void testMutableSimpleEquals() {
507 final ImmutableOffsetMap<String, String> source = createMap();
508 final Map<String, String> map = source.toModifiableMap();
510 assertTrue(map.equals(map));
511 assertFalse(map.equals(null));
512 assertFalse(map.equals("string"));
513 assertTrue(map.equals(source));
517 public void testMutableSimpleHashCode() {
518 final Map<String, String> map = createMap().toModifiableMap();
520 assertEquals(twoEntryMap.hashCode(), map.hashCode());
524 public void testMutableIteratorBasics() {
525 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
526 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
528 // Not advanced, remove should fail
529 assertThrows(IllegalStateException.class, () -> it.remove());
531 assertTrue(it.hasNext());
532 assertEquals("k1", it.next().getKey());
533 assertTrue(it.hasNext());
534 assertEquals("k2", it.next().getKey());
535 assertFalse(it.hasNext());
537 // Check end-of-iteration throw
538 assertThrows(NoSuchElementException.class, () -> it.next());
542 public void testMutableIteratorWithRemove() {
543 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
544 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
546 // Advance one element
547 assertTrue(it.hasNext());
548 assertEquals("k1", it.next().getKey());
552 assertEquals(1, map.size());
553 assertFalse(map.containsKey("k1"));
555 // Iterator should still work
556 assertTrue(it.hasNext());
557 assertEquals("k2", it.next().getKey());
558 assertFalse(it.hasNext());
562 public void testMutableIteratorOffsetReplaceWorks() {
563 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
564 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
567 map.put("k1", "new-v1");
568 assertTrue(it.hasNext());
572 public void testMutableIteratorNewReplaceWorks() {
573 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
575 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
578 map.put("k3", "new-v3");
579 assertTrue(it.hasNext());
583 public void testMutableIteratorOffsetAddBreaks() {
584 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
588 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
591 map.put("k1", "new-v1");
592 assertIteratorBroken(it);
596 public void testMutableIteratorNewAddBreaks() {
597 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
598 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
602 assertIteratorBroken(it);
606 public void testMutableIteratorOffsetRemoveBreaks() {
607 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
608 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
612 assertIteratorBroken(it);
616 public void testMutableIteratorNewRemoveBreaks() {
617 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
619 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
623 assertIteratorBroken(it);
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();
637 assertEquals(1, map.size());
639 // Check it2 was broken
640 assertIteratorBroken(it2);
644 public void testImmutableSerialization() throws IOException, ClassNotFoundException {
645 final Map<String, String> source = createMap();
647 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
648 try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {
649 oos.writeObject(source);
652 final ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
653 @SuppressWarnings("unchecked")
654 final Map<String, String> result = (Map<String, String>) ois.readObject();
656 assertEquals(source, result);