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.Iterators;
19 import java.io.ByteArrayInputStream;
20 import java.io.ByteArrayOutputStream;
21 import java.io.IOException;
22 import java.io.ObjectInputStream;
23 import java.io.ObjectOutputStream;
24 import java.util.AbstractMap.SimpleEntry;
25 import java.util.Collections;
26 import java.util.ConcurrentModificationException;
27 import java.util.Iterator;
29 import java.util.Map.Entry;
30 import java.util.NoSuchElementException;
32 import org.junit.Before;
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);
45 OffsetMapCache.invalidateCache();
48 @Test(expected=IllegalArgumentException.class)
49 public void testWrongImmutableConstruction() {
50 new ImmutableOffsetMap.Ordered<String, String>(Collections.<String, Integer>emptyMap(), new String[1]);
54 public void testCopyEmptyMap() {
55 final Map<String, String> source = Collections.emptyMap();
56 final Map<String, String> result = ImmutableOffsetMap.copyOf(source);
58 assertEquals(source, result);
59 assertTrue(result instanceof ImmutableMap);
63 public void testCopySingletonMap() {
64 final Map<String, String> source = Collections.singletonMap("a", "b");
65 final Map<String, String> result = ImmutableOffsetMap.copyOf(source);
67 assertEquals(source, result);
68 assertTrue(result instanceof SharedSingletonMap);
72 public void testCopyMap() {
73 final ImmutableOffsetMap<String, String> map = createMap();
75 // Equality in both directions
76 assertEquals(twoEntryMap, map);
77 assertEquals(map, twoEntryMap);
79 // hashcode has to match
80 assertEquals(twoEntryMap.hashCode(), map.hashCode());
82 // Iterator order needs to be preserved
83 assertTrue(Iterators.elementsEqual(twoEntryMap.entrySet().iterator(), map.entrySet().iterator()));
85 // Should result in the same object
86 assertSame(map, ImmutableOffsetMap.copyOf(map));
88 final Map<String, String> mutable = map.toModifiableMap();
89 final Map<String, String> copy = ImmutableOffsetMap.copyOf(mutable);
91 assertEquals(mutable, copy);
92 assertEquals(map, copy);
93 assertNotSame(mutable, copy);
94 assertNotSame(map, copy);
98 public void testImmutableSimpleEquals() {
99 final Map<String, String> map = createMap();
101 assertTrue(map.equals(map));
102 assertFalse(map.equals(null));
103 assertFalse(map.equals("string"));
107 public void testImmutableGet() {
108 final Map<String, String> map = createMap();
110 assertEquals("v1", map.get("k1"));
111 assertEquals("v2", map.get("k2"));
112 assertNull(map.get("non-existent"));
113 assertNull(map.get(null));
117 public void testImmutableGuards() {
118 final Map<String, String> map = createMap();
121 map.values().add("v1");
123 } catch (UnsupportedOperationException e) {
127 map.values().remove("v1");
129 } catch (UnsupportedOperationException e) {
133 map.values().clear();
135 } catch (UnsupportedOperationException e) {
139 final Iterator<String> it = map.values().iterator();
143 } catch (UnsupportedOperationException e) {
147 map.keySet().add("k1");
149 } catch (UnsupportedOperationException e) {
153 map.keySet().clear();
155 } catch (UnsupportedOperationException e) {
159 map.keySet().remove("k1");
161 } catch (UnsupportedOperationException e) {
165 final Iterator<String> it = map.keySet().iterator();
169 } catch (UnsupportedOperationException e) {
173 map.entrySet().clear();
175 } catch (UnsupportedOperationException e) {
179 map.entrySet().add(new SimpleEntry<>("k1", "v1"));
181 } catch (UnsupportedOperationException e) {
185 map.entrySet().remove(new SimpleEntry<>("k1", "v1"));
187 } catch (UnsupportedOperationException e) {
191 final Iterator<Entry<String, String>> it = map.entrySet().iterator();
195 } catch (UnsupportedOperationException e) {
201 } catch (UnsupportedOperationException e) {
205 map.put("k1", "fail");
207 } catch (UnsupportedOperationException e) {
211 map.putAll(ImmutableMap.of("k1", "fail"));
213 } catch (UnsupportedOperationException e) {
219 } catch (UnsupportedOperationException e) {
224 public void testMutableGet() {
225 final Map<String, String> map = createMap().toModifiableMap();
228 assertEquals("v1", map.get("k1"));
229 assertEquals("v2", map.get("k2"));
230 assertEquals("v3", map.get("k3"));
231 assertNull(map.get("non-existent"));
232 assertNull(map.get(null));
236 public void testImmutableSize() {
237 final Map<String, String> map = createMap();
238 assertEquals(2, map.size());
242 public void testImmutableIsEmpty() {
243 final Map<String, String> map = createMap();
244 assertFalse(map.isEmpty());
248 public void testImmutableContains() {
249 final Map<String, String> map = createMap();
250 assertTrue(map.containsKey("k1"));
251 assertTrue(map.containsKey("k2"));
252 assertFalse(map.containsKey("non-existent"));
253 assertFalse(map.containsKey(null));
254 assertTrue(map.containsValue("v1"));
255 assertFalse(map.containsValue("non-existent"));
259 public void testImmutableEquals() {
260 final Map<String, String> map = createMap();
262 assertFalse(map.equals(threeEntryMap));
263 assertFalse(map.equals(ImmutableMap.of("k1", "v1", "k3", "v3")));
264 assertFalse(map.equals(ImmutableMap.of("k1", "v1", "k2", "different-value")));
268 public void testMutableContains() {
269 final Map<String, String> map = createMap().toModifiableMap();
271 assertTrue(map.containsKey("k1"));
272 assertTrue(map.containsKey("k2"));
273 assertTrue(map.containsKey("k3"));
274 assertFalse(map.containsKey("non-existent"));
275 assertFalse(map.containsKey(null));
279 public void testtoModifiableMap() {
280 final ImmutableOffsetMap<String, String> source = createMap();
281 final Map<String, String> result = source.toModifiableMap();
283 // The two maps should be equal, but isolated
284 assertTrue(result instanceof MutableOffsetMap);
285 assertEquals(source, result);
286 assertEquals(result, source);
288 // Quick test for clearing MutableOffsetMap
290 assertEquals(0, result.size());
291 assertEquals(Collections.emptyMap(), result);
293 // The two maps should differ now
294 assertFalse(source.equals(result));
295 assertFalse(result.equals(source));
297 // The source map should still equal the template
298 assertEquals(twoEntryMap, source);
299 assertEquals(source, twoEntryMap);
303 public void testReusedFields() {
304 final ImmutableOffsetMap<String, String> source = createMap();
305 final MutableOffsetMap<String, String> mutable = source.toModifiableMap();
307 // Should not affect the result
308 mutable.remove("non-existent");
310 // Resulting map should be equal, but not the same object
311 final ImmutableOffsetMap<String, String> result = (ImmutableOffsetMap<String, String>) mutable.toUnmodifiableMap();
312 assertNotSame(source, result);
313 assertEquals(source, result);
315 // Internal fields should be reused
316 assertSame(source.offsets(), result.offsets());
317 assertSame(source.objects(), result.objects());
321 public void testReusedOffsets() {
322 final ImmutableOffsetMap<String, String> source = createMap();
323 final MutableOffsetMap<String, String> mutable = source.toModifiableMap();
325 mutable.remove("k1");
326 mutable.put("k1", "v1");
328 final ImmutableOffsetMap<String, String> result = (ImmutableOffsetMap<String, String>) mutable.toUnmodifiableMap();
329 assertEquals(source, result);
331 // Only offsets should be shared
332 assertSame(source.offsets(), result.offsets());
333 assertNotSame(source.objects(), result.objects());
335 // Iterator order needs to be preserved
336 assertTrue(Iterators.elementsEqual(source.entrySet().iterator(), result.entrySet().iterator()));
340 public void testEmptyMutable() throws CloneNotSupportedException {
341 final MutableOffsetMap<String, String> map = MutableOffsetMap.of();
342 assertTrue(map.isEmpty());
344 final Map<String, String> other = map.clone();
345 assertEquals(other, map);
346 assertNotSame(other, map);
350 public void testMutableToEmpty() {
351 final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
353 mutable.remove("k1");
354 mutable.remove("k2");
356 assertTrue(mutable.isEmpty());
357 assertSame(ImmutableMap.of(), mutable.toUnmodifiableMap());
361 public void testMutableToSingleton() {
362 final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
364 mutable.remove("k1");
366 final Map<String, String> result = mutable.toUnmodifiableMap();
368 // Should devolve to a singleton
369 assertTrue(result instanceof ImmutableMap);
370 assertEquals(ImmutableMap.of("k2", "v2"), result);
374 public void testMutableToNewSingleton() {
375 final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
377 mutable.remove("k1");
378 mutable.put("k3", "v3");
380 final Map<String, String> result = mutable.toUnmodifiableMap();
382 assertTrue(result instanceof ImmutableOffsetMap);
383 assertEquals(ImmutableMap.of("k2", "v2", "k3", "v3"), result);
387 public void testMutableSize() {
388 final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
389 assertEquals(2, mutable.size());
391 mutable.put("k3", "v3");
392 assertEquals(3, mutable.size());
393 mutable.remove("k2");
394 assertEquals(2, mutable.size());
395 mutable.put("k1", "new-v1");
396 assertEquals(2, mutable.size());
400 public void testExpansionWithOrder() {
401 final MutableOffsetMap<String, String> mutable = createMap().toModifiableMap();
403 mutable.remove("k1");
404 mutable.put("k3", "v3");
405 mutable.put("k1", "v1");
407 assertEquals(ImmutableMap.of("k3", "v3"), mutable.newKeys());
409 final Map<String, String> result = mutable.toUnmodifiableMap();
411 assertTrue(result instanceof ImmutableOffsetMap);
412 assertEquals(threeEntryMap, result);
413 assertEquals(result, threeEntryMap);
414 assertTrue(Iterators.elementsEqual(threeEntryMap.entrySet().iterator(), result.entrySet().iterator()));
418 public void testReplacedValue() {
419 final ImmutableOffsetMap<String, String> source = createMap();
420 final MutableOffsetMap<String, String> mutable = source.toModifiableMap();
422 mutable.put("k1", "replaced");
424 final ImmutableOffsetMap<String, String> result = (ImmutableOffsetMap<String, String>) mutable.toUnmodifiableMap();
425 final Map<String, String> reference = ImmutableMap.of("k1", "replaced", "k2", "v2");
427 assertEquals(reference, result);
428 assertEquals(result, reference);
429 assertSame(source.offsets(), result.offsets());
430 assertNotSame(source.objects(), result.objects());
434 public void testCloneableFlipping() throws CloneNotSupportedException {
435 final MutableOffsetMap<String, String> source = createMap().toModifiableMap();
437 // Must clone before mutation
438 assertTrue(source.needClone());
440 // Non-existent entry, should not clone
441 source.remove("non-existent");
442 assertTrue(source.needClone());
444 // Changes the array, should clone
446 assertFalse(source.needClone());
448 // Create a clone of the map, which shares the array
449 final MutableOffsetMap<String, String> result = source.clone();
450 assertFalse(source.needClone());
451 assertTrue(result.needClone());
452 assertSame(source.array(), result.array());
454 // Changes the array, should clone
455 source.put("k1", "v2");
456 assertFalse(source.needClone());
457 assertTrue(result.needClone());
459 // Creates a immutable view, which shares the array
460 final ImmutableOffsetMap<String, String> immutable = (ImmutableOffsetMap<String, String>) source.toUnmodifiableMap();
461 assertTrue(source.needClone());
462 assertSame(source.array(), immutable.objects());
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);