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.Iterables;
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.Before;
34 import org.junit.Test;
36 public class OffsetMapTest {
37 private final Map<String, String> twoEntryMap = ImmutableMap.of("k1", "v1", "k2", "v2");
38 private final Map<String, String> threeEntryMap = ImmutableMap.of("k1", "v1", "k2", "v2", "k3", "v3");
40 private ImmutableOffsetMap<String, String> createMap() {
41 return (ImmutableOffsetMap<String, String>) ImmutableOffsetMap.copyOf(twoEntryMap);
46 OffsetMapCache.invalidateCache();
49 @Test(expected=IllegalArgumentException.class)
50 public void testWrongImmutableConstruction() {
51 new ImmutableOffsetMap.Ordered<String, String>(Collections.<String, Integer>emptyMap(), new String[1]);
55 public void testCopyEmptyMap() {
56 final Map<String, String> source = Collections.emptyMap();
57 final Map<String, String> result = ImmutableOffsetMap.copyOf(source);
59 assertEquals(source, result);
60 assertTrue(result instanceof ImmutableMap);
64 public void testCopySingletonMap() {
65 final Map<String, String> source = Collections.singletonMap("a", "b");
66 final Map<String, String> result = ImmutableOffsetMap.copyOf(source);
68 assertEquals(source, result);
69 assertTrue(result instanceof SharedSingletonMap);
73 public void testCopyMap() {
74 final ImmutableOffsetMap<String, String> map = createMap();
76 // Equality in both directions
77 assertEquals(twoEntryMap, map);
78 assertEquals(map, twoEntryMap);
80 // hashcode has to match
81 assertEquals(twoEntryMap.hashCode(), map.hashCode());
83 // Iterator order needs to be preserved
84 assertTrue(Iterators.elementsEqual(twoEntryMap.entrySet().iterator(), map.entrySet().iterator()));
86 // Should result in the same object
87 assertSame(map, ImmutableOffsetMap.copyOf(map));
89 final Map<String, String> mutable = map.toModifiableMap();
90 final Map<String, String> copy = ImmutableOffsetMap.copyOf(mutable);
92 assertEquals(mutable, copy);
93 assertEquals(map, copy);
94 assertNotSame(mutable, copy);
95 assertNotSame(map, copy);
99 public void testImmutableSimpleEquals() {
100 final Map<String, String> map = createMap();
102 assertTrue(map.equals(map));
103 assertFalse(map.equals(null));
104 assertFalse(map.equals("string"));
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 assertTrue(source.equals(result));
331 assertTrue(result.equals(source));
333 // Iterator order must not be preserved
334 assertFalse(Iterators.elementsEqual(source.entrySet().iterator(), result.entrySet().iterator()));
338 public void testEmptyMutable() throws CloneNotSupportedException {
339 final MutableOffsetMap<String, String> map = MutableOffsetMap.of();
340 assertTrue(map.isEmpty());
342 final Map<String, String> other = map.clone();
343 assertEquals(other, map);
344 assertNotSame(other, map);
348 public void testMutableToEmpty() {
349 final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
351 mutable.remove("k1");
352 mutable.remove("k2");
354 assertTrue(mutable.isEmpty());
355 assertSame(ImmutableMap.of(), mutable.toUnmodifiableMap());
359 public void testMutableToSingleton() {
360 final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
362 mutable.remove("k1");
364 final Map<String, String> result = mutable.toUnmodifiableMap();
366 // Should devolve to a singleton
367 assertTrue(result instanceof SharedSingletonMap);
368 assertEquals(ImmutableMap.of("k2", "v2"), result);
372 public void testMutableToNewSingleton() {
373 final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
375 mutable.remove("k1");
376 mutable.put("k3", "v3");
378 final Map<String, String> result = mutable.toUnmodifiableMap();
380 assertTrue(result instanceof ImmutableOffsetMap);
381 assertEquals(ImmutableMap.of("k2", "v2", "k3", "v3"), result);
385 public void testMutableSize() {
386 final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
387 assertEquals(2, mutable.size());
389 mutable.put("k3", "v3");
390 assertEquals(3, mutable.size());
391 mutable.remove("k2");
392 assertEquals(2, mutable.size());
393 mutable.put("k1", "new-v1");
394 assertEquals(2, mutable.size());
398 public void testExpansionWithOrder() {
399 final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
401 mutable.remove("k1");
402 mutable.put("k3", "v3");
403 mutable.put("k1", "v1");
405 assertEquals(ImmutableMap.of("k1", "v1", "k3", "v3"), mutable.newKeys());
407 final Map<String, String> result = mutable.toUnmodifiableMap();
409 assertTrue(result instanceof ImmutableOffsetMap);
410 assertEquals(threeEntryMap, result);
411 assertEquals(result, threeEntryMap);
412 assertFalse(Iterators.elementsEqual(threeEntryMap.entrySet().iterator(), result.entrySet().iterator()));
416 public void testReplacedValue() {
417 final ImmutableOffsetMap<String, String> source = createMap();
418 final MutableOffsetMap<String, String> mutable = source.toModifiableMap();
420 mutable.put("k1", "replaced");
422 final ImmutableOffsetMap<String, String> result = (ImmutableOffsetMap<String, String>) mutable.toUnmodifiableMap();
423 final Map<String, String> reference = ImmutableMap.of("k1", "replaced", "k2", "v2");
425 assertEquals(reference, result);
426 assertEquals(result, reference);
427 assertSame(source.offsets(), result.offsets());
428 assertNotSame(source.objects(), result.objects());
432 public void testCloneableFlipping() throws CloneNotSupportedException {
433 final MutableOffsetMap<String, String> source = createMap().toModifiableMap();
435 // Must clone before mutation
436 assertTrue(source.needClone());
438 // Non-existent entry, should not clone
439 source.remove("non-existent");
440 assertTrue(source.needClone());
442 // Changes the array, should clone
444 assertFalse(source.needClone());
446 // Create a clone of the map, which shares the array
447 final MutableOffsetMap<String, String> result = source.clone();
448 assertFalse(source.needClone());
449 assertTrue(result.needClone());
450 assertSame(source.array(), result.array());
452 // Changes the array, should clone
453 source.put("k1", "v2");
454 assertFalse(source.needClone());
455 assertTrue(result.needClone());
457 // Forced copy, no cloning needed, but maps are equal
458 final ImmutableOffsetMap<String, String> immutable = (ImmutableOffsetMap<String, String>) source.toUnmodifiableMap();
459 assertFalse(source.needClone());
460 assertTrue(source.equals(immutable));
461 assertTrue(immutable.equals(source));
462 assertTrue(Iterables.elementsEqual(source.entrySet(), immutable.entrySet()));
466 public void testMutableEntrySet() {
467 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
469 assertTrue(map.entrySet().add(new SimpleEntry<>("k3", "v3")));
470 assertTrue(map.containsKey("k3"));
471 assertEquals("v3", map.get("k3"));
473 // null is not an Entry: ignore
474 assertFalse(map.entrySet().remove(null));
476 // non-matching value: ignore
477 assertFalse(map.entrySet().remove(new SimpleEntry<>("k1", "other")));
478 assertTrue(map.containsKey("k1"));
480 // ignore null values
481 assertFalse(map.entrySet().remove(new SimpleEntry<>("k1", null)));
482 assertTrue(map.containsKey("k1"));
484 assertTrue(map.entrySet().remove(new SimpleEntry<>("k1", "v1")));
485 assertFalse(map.containsKey("k1"));
488 private static void assertIteratorBroken(final Iterator<?> it) {
492 } catch (ConcurrentModificationException e) {
497 } catch (ConcurrentModificationException e) {
502 } catch (ConcurrentModificationException e) {
507 public void testMutableSimpleEquals() {
508 final ImmutableOffsetMap<String, String> source = createMap();
509 final Map<String, String> map = source.toModifiableMap();
511 assertTrue(map.equals(map));
512 assertFalse(map.equals(null));
513 assertFalse(map.equals("string"));
514 assertTrue(map.equals(source));
518 public void testMutableSimpleHashCode() {
519 final Map<String, String> map = createMap().toModifiableMap();
521 assertEquals(twoEntryMap.hashCode(), map.hashCode());
525 public void testMutableIteratorBasics() {
526 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
527 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
529 // Not advanced, remove should fail
533 } catch (IllegalStateException e) {
536 assertTrue(it.hasNext());
537 assertEquals("k1", it.next().getKey());
538 assertTrue(it.hasNext());
539 assertEquals("k2", it.next().getKey());
540 assertFalse(it.hasNext());
542 // Check end-of-iteration throw
546 } catch (NoSuchElementException e) {
551 public void testMutableIteratorWithRemove() {
552 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
553 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
555 // Advance one element
556 assertTrue(it.hasNext());
557 assertEquals("k1", it.next().getKey());
561 assertEquals(1, map.size());
562 assertFalse(map.containsKey("k1"));
564 // Iterator should still work
565 assertTrue(it.hasNext());
566 assertEquals("k2", it.next().getKey());
567 assertFalse(it.hasNext());
571 public void testMutableIteratorOffsetReplaceWorks() {
572 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
573 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
576 map.put("k1", "new-v1");
577 assertTrue(it.hasNext());
581 public void testMutableIteratorNewReplaceWorks() {
582 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
584 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
587 map.put("k3", "new-v3");
588 assertTrue(it.hasNext());
592 public void testMutableIteratorOffsetAddBreaks() {
593 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
597 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
600 map.put("k1", "new-v1");
601 assertIteratorBroken(it);
605 public void testMutableIteratorNewAddBreaks() {
606 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
607 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
611 assertIteratorBroken(it);
615 public void testMutableIteratorOffsetRemoveBreaks() {
616 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
617 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
621 assertIteratorBroken(it);
625 public void testMutableIteratorNewRemoveBreaks() {
626 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
628 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
632 assertIteratorBroken(it);
636 public void testMutableCrossIteratorRemove() {
637 final MutableOffsetMap<String, String> map = createMap().toModifiableMap();
638 final Set<Entry<String, String>> es = map.entrySet();
639 final Iterator<Entry<String, String>> it1 = es.iterator();
640 final Iterator<Entry<String, String>> it2 = es.iterator();
646 assertEquals(1, map.size());
648 // Check it2 was broken
649 assertIteratorBroken(it2);
653 public void testImmutableSerialization() throws IOException, ClassNotFoundException {
654 final Map<String, String> source = createMap();
656 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
657 try (final ObjectOutputStream oos = new ObjectOutputStream(bos)) {
658 oos.writeObject(source);
661 final ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
662 @SuppressWarnings("unchecked")
663 final Map<String, String> result = (Map<String, String>) ois.readObject();
665 assertEquals(source, result);