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 // Iterator order needs to be preserved
75 assertTrue(Iterators.elementsEqual(twoEntryMap.entrySet().iterator(), map.entrySet().iterator()));
77 // Should result in the same object
78 assertSame(map, ImmutableOffsetMap.copyOf(map));
80 final Map<String, String> mutable = map.toModifiableMap();
81 final Map<String, String> copy = ImmutableOffsetMap.copyOf(mutable);
83 assertEquals(mutable, copy);
84 assertEquals(map, copy);
85 assertNotSame(mutable, copy);
86 assertNotSame(map, copy);
90 public void testImmutableSimpleEquals() {
91 final Map<String, String> map = createMap();
93 assertTrue(map.equals(map));
94 assertFalse(map.equals(null));
95 assertFalse(map.equals("string"));
99 public void testImmutableCopyConstructor() {
100 final ImmutableOffsetMap<String, String> source = createMap();
101 final ImmutableOffsetMap<String, String> result = new ImmutableOffsetMap<>(source);
103 assertSame(source.offsets(), result.offsets());
104 assertSame(source.objects(), result.objects());
108 public void testImmutableGet() {
109 final Map<String, String> map = createMap();
111 assertEquals("v1", map.get("k1"));
112 assertEquals("v2", map.get("k2"));
113 assertNull(map.get("non-existent"));
114 assertNull(map.get(null));
118 public void testImmutableGuards() {
119 final Map<String, String> map = createMap();
122 map.values().add("v1");
124 } catch (UnsupportedOperationException e) {
128 map.values().remove("v1");
130 } catch (UnsupportedOperationException e) {
134 map.values().clear();
136 } catch (UnsupportedOperationException e) {
140 final Iterator<String> it = map.values().iterator();
144 } catch (UnsupportedOperationException e) {
148 map.keySet().add("k1");
150 } catch (UnsupportedOperationException e) {
154 map.keySet().clear();
156 } catch (UnsupportedOperationException e) {
160 map.keySet().remove("k1");
162 } catch (UnsupportedOperationException e) {
166 final Iterator<String> it = map.keySet().iterator();
170 } catch (UnsupportedOperationException e) {
174 map.entrySet().clear();
176 } catch (UnsupportedOperationException e) {
180 map.entrySet().add(new SimpleEntry<>("k1", "v1"));
182 } catch (UnsupportedOperationException e) {
186 map.entrySet().remove(new SimpleEntry<>("k1", "v1"));
188 } catch (UnsupportedOperationException e) {
192 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
196 } catch (UnsupportedOperationException e) {
202 } catch (UnsupportedOperationException e) {
206 map.put("k1", "fail");
208 } catch (UnsupportedOperationException e) {
212 map.putAll(ImmutableMap.of("k1", "fail"));
214 } catch (UnsupportedOperationException e) {
220 } catch (UnsupportedOperationException e) {
225 public void testMutableGet() {
226 final Map<String, String> map = createMap().toModifiableMap();
229 assertEquals("v1", map.get("k1"));
230 assertEquals("v2", map.get("k2"));
231 assertEquals("v3", map.get("k3"));
232 assertNull(map.get("non-existent"));
233 assertNull(map.get(null));
237 public void testImmutableSize() {
238 final Map<String, String> map = createMap();
239 assertEquals(2, map.size());
243 public void testImmutableIsEmpty() {
244 final Map<String, String> map = createMap();
245 assertFalse(map.isEmpty());
249 public void testImmutableContains() {
250 final Map<String, String> map = createMap();
251 assertTrue(map.containsKey("k1"));
252 assertTrue(map.containsKey("k2"));
253 assertFalse(map.containsKey("non-existent"));
254 assertFalse(map.containsKey(null));
255 assertTrue(map.containsValue("v1"));
256 assertFalse(map.containsValue("non-existent"));
260 public void testImmutableEquals() {
261 final Map<String, String> map = createMap();
263 assertFalse(map.equals(threeEntryMap));
264 assertFalse(map.equals(ImmutableMap.of("k1", "v1", "k3", "v3")));
265 assertFalse(map.equals(ImmutableMap.of("k1", "v1", "k2", "different-value")));
269 public void testMutableContains() {
270 final Map<String, String> map = createMap().toModifiableMap();
272 assertTrue(map.containsKey("k1"));
273 assertTrue(map.containsKey("k2"));
274 assertTrue(map.containsKey("k3"));
275 assertFalse(map.containsKey("non-existent"));
276 assertFalse(map.containsKey(null));
280 public void testtoModifiableMap() {
281 final ImmutableOffsetMap<String, String> source = createMap();
282 final Map<String, String> result = source.toModifiableMap();
284 // The two maps should be equal, but isolated
285 assertTrue(result instanceof MutableOffsetMap);
286 assertEquals(source, result);
287 assertEquals(result, source);
289 // Quick test for clearing MutableOffsetMap
291 assertEquals(0, result.size());
292 assertEquals(Collections.emptyMap(), result);
294 // The two maps should differ now
295 assertFalse(source.equals(result));
296 assertFalse(result.equals(source));
298 // The source map should still equal the template
299 assertEquals(twoEntryMap, source);
300 assertEquals(source, twoEntryMap);
304 public void testReusedFields() {
305 final ImmutableOffsetMap<String, String> source = createMap();
306 final MutableOffsetMap<String, String> mutable = source.toModifiableMap();
308 // Should not affect the result
309 mutable.remove("non-existent");
311 // Resulting map should be equal, but not the same object
312 final ImmutableOffsetMap<String, String> result = (ImmutableOffsetMap<String, String>) mutable.toUnmodifiableMap();
313 assertNotSame(source, result);
314 assertEquals(source, result);
316 // Internal fields should be reused
317 assertSame(source.offsets(), result.offsets());
318 assertSame(source.objects(), result.objects());
322 public void testReusedOffsets() {
323 final ImmutableOffsetMap<String, String> source = createMap();
324 final MutableOffsetMap<String, String> mutable = source.toModifiableMap();
326 mutable.remove("k1");
327 mutable.put("k1", "v1");
329 final ImmutableOffsetMap<String, String> result = (ImmutableOffsetMap<String, String>) mutable.toUnmodifiableMap();
330 assertEquals(source, result);
332 // Only offsets should be shared
333 assertSame(source.offsets(), result.offsets());
334 assertNotSame(source.objects(), result.objects());
336 // Iterator order needs to be preserved
337 assertTrue(Iterators.elementsEqual(source.entrySet().iterator(), result.entrySet().iterator()));
341 public void testEmptyMutable() throws CloneNotSupportedException {
342 final MutableOffsetMap<String, String> map = new MutableOffsetMap<>();
343 assertTrue(map.isEmpty());
345 final Map<String, String> other = map.clone();
346 assertEquals(other, map);
347 assertNotSame(other, map);
351 public void testMutableWithKeyset() {
352 final MutableOffsetMap<String, String> map = new MutableOffsetMap<>(ImmutableSet.of("k1", "k2"));
353 assertTrue(map.isEmpty());
354 assertTrue(map.keySet().isEmpty());
355 assertNull(map.get("k1"));
356 assertNull(map.remove("k2"));
360 public void testMutableToEmpty() {
361 final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
363 mutable.remove("k1");
364 mutable.remove("k2");
366 assertTrue(mutable.isEmpty());
367 assertSame(ImmutableMap.of(), mutable.toUnmodifiableMap());
371 public void testMutableToSingleton() {
372 final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
374 mutable.remove("k1");
376 final Map<String, String> result = mutable.toUnmodifiableMap();
378 // Should devolve to a singleton
379 assertTrue(result instanceof ImmutableMap);
380 assertEquals(ImmutableMap.of("k2", "v2"), result);
384 public void testMutableToNewSingleton() {
385 final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
387 mutable.remove("k1");
388 mutable.put("k3", "v3");
390 final Map<String, String> result = mutable.toUnmodifiableMap();
392 assertTrue(result instanceof ImmutableOffsetMap);
393 assertEquals(ImmutableMap.of("k2", "v2", "k3", "v3"), result);
397 public void testMutableSize() {
398 final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
399 assertEquals(2, mutable.size());
401 mutable.put("k3", "v3");
402 assertEquals(3, mutable.size());
403 mutable.remove("k2");
404 assertEquals(2, mutable.size());
405 mutable.put("k1", "new-v1");
406 assertEquals(2, mutable.size());
410 public void testExpansionWithOrder() {
411 final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
413 mutable.remove("k1");
414 mutable.put("k3", "v3");
415 mutable.put("k1", "v1");
417 assertEquals(ImmutableMap.of("k3", "v3"), mutable.newKeys());
419 final Map<String, String> result = mutable.toUnmodifiableMap();
421 assertTrue(result instanceof ImmutableOffsetMap);
422 assertEquals(threeEntryMap, result);
423 assertEquals(result, threeEntryMap);
424 assertTrue(Iterators.elementsEqual(threeEntryMap.entrySet().iterator(), result.entrySet().iterator()));
428 public void testReplacedValue() {
429 final ImmutableOffsetMap<String, String> source = createMap();
430 final MutableOffsetMap<String, String> mutable = source.toModifiableMap();
432 mutable.put("k1", "replaced");
434 final ImmutableOffsetMap<String, String> result = (ImmutableOffsetMap<String, String>) mutable.toUnmodifiableMap();
435 final Map<String, String> reference = ImmutableMap.of("k1", "replaced", "k2", "v2");
437 assertEquals(reference, result);
438 assertEquals(result, reference);
439 assertSame(source.offsets(), result.offsets());
440 assertNotSame(source.objects(), result.objects());
444 public void testCloneableFlipping() throws CloneNotSupportedException {
445 final MutableOffsetMap<String, String> source = createMap().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 = (ImmutableOffsetMap<String, String>) source.toUnmodifiableMap();
471 assertTrue(source.needClone());
472 assertSame(source.array(), immutable.objects());
476 public void testMutableEntrySet() {
477 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
479 assertTrue(map.entrySet().add(new SimpleEntry<>("k3", "v3")));
480 assertTrue(map.containsKey("k3"));
481 assertEquals("v3", map.get("k3"));
483 // null is not an Entry: ignore
484 assertFalse(map.entrySet().remove(null));
486 // non-matching value: ignore
487 assertFalse(map.entrySet().remove(new SimpleEntry<>("k1", "other")));
488 assertTrue(map.containsKey("k1"));
490 // ignore null values
491 assertFalse(map.entrySet().remove(new SimpleEntry<>("k1", null)));
492 assertTrue(map.containsKey("k1"));
494 assertTrue(map.entrySet().remove(new SimpleEntry<>("k1", "v1")));
495 assertFalse(map.containsKey("k1"));
498 private static void assertIteratorBroken(final Iterator<?> it) {
502 } catch (ConcurrentModificationException e) {
507 } catch (ConcurrentModificationException e) {
512 } catch (ConcurrentModificationException e) {
517 public void testMutableSimpleEquals() {
518 final ImmutableOffsetMap<String, String> source = createMap();
519 final Map<String, String> map = source.toModifiableMap();
521 assertTrue(map.equals(map));
522 assertFalse(map.equals(null));
523 assertFalse(map.equals("string"));
524 assertTrue(map.equals(source));
528 public void testMutableIteratorBasics() {
529 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
530 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
532 // Not advanced, remove should fail
536 } catch (IllegalStateException e) {
539 assertTrue(it.hasNext());
540 assertEquals("k1", it.next().getKey());
541 assertTrue(it.hasNext());
542 assertEquals("k2", it.next().getKey());
543 assertFalse(it.hasNext());
545 // Check end-of-iteration throw
549 } catch (NoSuchElementException e) {
554 public void testMutableIteratorWithRemove() {
555 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
556 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
558 // Advance one element
559 assertTrue(it.hasNext());
560 assertEquals("k1", it.next().getKey());
564 assertEquals(1, map.size());
565 assertFalse(map.containsKey("k1"));
567 // Iterator should still work
568 assertTrue(it.hasNext());
569 assertEquals("k2", it.next().getKey());
570 assertFalse(it.hasNext());
574 public void testMutableIteratorOffsetReplaceWorks() {
575 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
576 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
579 map.put("k1", "new-v1");
580 assertTrue(it.hasNext());
584 public void testMutableIteratorNewReplaceWorks() {
585 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
587 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
590 map.put("k3", "new-v3");
591 assertTrue(it.hasNext());
595 public void testMutableIteratorOffsetAddBreaks() {
596 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
600 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
603 map.put("k1", "new-v1");
604 assertIteratorBroken(it);
608 public void testMutableIteratorNewAddBreaks() {
609 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
610 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
614 assertIteratorBroken(it);
618 public void testMutableIteratorOffsetRemoveBreaks() {
619 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
620 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
624 assertIteratorBroken(it);
628 public void testMutableIteratorNewRemoveBreaks() {
629 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
631 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
635 assertIteratorBroken(it);
639 public void testMutableCrossIteratorRemove() {
640 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
641 final Set<Entry<String, String>> es = map.entrySet();
642 final Iterator<Entry<String, String>> it1 = es.iterator();
643 final Iterator<Entry<String, String>> it2 = es.iterator();
649 assertEquals(1, map.size());
651 // Check it2 was broken
652 assertIteratorBroken(it2);
656 public void testImmutableSerialization() throws IOException, ClassNotFoundException {
657 final Map<String, String> source = createMap();
659 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
660 try (final ObjectOutputStream oos = new ObjectOutputStream(bos)) {
661 oos.writeObject(source);
664 final ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
665 @SuppressWarnings("unchecked")
666 final Map<String, String> result = (Map<String, String>) ois.readObject();
668 assertEquals(source, result);