BUG-5280: expand ShardDataTree to cover transaction mechanics
[controller.git] / opendaylight / md-sal / sal-distributed-datastore / src / test / java / org / opendaylight / controller / cluster / datastore / DataChangeListenerSupportTest.java
1 /*
2  * Copyright (c) 2015 Brocade Communications Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.controller.cluster.datastore;
9
10 import static org.junit.Assert.assertEquals;
11 import static org.opendaylight.controller.md.cluster.datastore.model.TestModel.INNER_LIST_QNAME;
12 import static org.opendaylight.controller.md.cluster.datastore.model.TestModel.OUTER_CONTAINER_PATH;
13 import static org.opendaylight.controller.md.cluster.datastore.model.TestModel.OUTER_CONTAINER_QNAME;
14 import static org.opendaylight.controller.md.cluster.datastore.model.TestModel.OUTER_LIST_PATH;
15 import static org.opendaylight.controller.md.cluster.datastore.model.TestModel.OUTER_LIST_QNAME;
16 import static org.opendaylight.controller.md.cluster.datastore.model.TestModel.TEST_PATH;
17 import static org.opendaylight.controller.md.cluster.datastore.model.TestModel.TEST_QNAME;
18 import static org.opendaylight.controller.md.cluster.datastore.model.TestModel.innerEntryPath;
19 import static org.opendaylight.controller.md.cluster.datastore.model.TestModel.innerNode;
20 import static org.opendaylight.controller.md.cluster.datastore.model.TestModel.outerEntryKey;
21 import static org.opendaylight.controller.md.cluster.datastore.model.TestModel.outerEntryPath;
22 import static org.opendaylight.controller.md.cluster.datastore.model.TestModel.outerNode;
23 import static org.opendaylight.controller.md.cluster.datastore.model.TestModel.outerNodeEntry;
24 import static org.opendaylight.controller.md.cluster.datastore.model.TestModel.testNodeWithOuter;
25 import akka.actor.ActorRef;
26 import akka.testkit.TestActorRef;
27 import org.junit.After;
28 import org.junit.Before;
29 import org.junit.Test;
30 import org.opendaylight.controller.cluster.datastore.messages.RegisterChangeListener;
31 import org.opendaylight.controller.cluster.datastore.utils.MockDataChangeListener;
32 import org.opendaylight.controller.cluster.raft.TestActorFactory;
33 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
34 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
35 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodes;
36 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
37
38 /**
39  * Unit tests for DataChangeListenerSupport.
40  *
41  * @author Thomas Pantelis
42  */
43 public class DataChangeListenerSupportTest extends AbstractShardTest {
44     private final TestActorFactory actorFactory = new TestActorFactory(getSystem());
45
46     private Shard shard;
47     private DataChangeListenerSupport support;
48
49     @Before
50     public void setup() throws InterruptedException {
51         shard = createShard();
52         support = new DataChangeListenerSupport(shard);
53     }
54
55     @Override
56     @After
57     public void tearDown() {
58         super.tearDown();
59         actorFactory.close();
60     }
61
62     @Test
63     public void testChangeListenerWithNoInitialData() throws Exception {
64         MockDataChangeListener listener = registerChangeListener(TEST_PATH, DataChangeScope.ONE, 0, true);
65
66         listener.expectNoMoreChanges("Unexpected initial change event");
67     }
68
69     @Test
70     public void testInitialChangeListenerEventWithContainerPath() throws Exception {
71         writeToStore(shard.getDataStore(), TEST_PATH, ImmutableNodes.containerNode(TEST_QNAME));
72
73         MockDataChangeListener listener = registerChangeListener(TEST_PATH, DataChangeScope.ONE, 1, true);
74
75         listener.waitForChangeEvents(TEST_PATH);
76     }
77
78     @Test
79     public void testInitialChangeListenerEventWithListPath() throws Exception {
80         mergeToStore(shard.getDataStore(), TEST_PATH, testNodeWithOuter(1, 2));
81
82         MockDataChangeListener listener = registerChangeListener(OUTER_LIST_PATH, DataChangeScope.ONE, 1, true);
83
84         listener.waitForChangeEvents();
85         assertEquals("Outer entry 1 present", true, NormalizedNodes.findNode(
86                 listener.getCreatedData(0, OUTER_LIST_PATH), outerEntryKey(1)).isPresent());
87         assertEquals("Outer entry 2 present", true, NormalizedNodes.findNode(
88                 listener.getCreatedData(0, OUTER_LIST_PATH), outerEntryKey(2)).isPresent());
89     }
90
91     @Test
92     public void testInitialChangeListenerEventWithWildcardedListPath() throws Exception {
93         mergeToStore(shard.getDataStore(), TEST_PATH, testNodeWithOuter(1, 2));
94         writeToStore(shard.getDataStore(), OUTER_CONTAINER_PATH, ImmutableNodes.containerNode(OUTER_CONTAINER_QNAME));
95
96         MockDataChangeListener listener = registerChangeListener(OUTER_LIST_PATH.node(OUTER_LIST_QNAME),
97                 DataChangeScope.ONE, 1, true);
98
99         listener.waitForChangeEvents();
100         listener.verifyCreatedData(0, outerEntryPath(1));
101         listener.verifyCreatedData(0, outerEntryPath(2));
102         listener.verifyNoCreatedData(0, OUTER_CONTAINER_PATH);
103     }
104
105     @Test
106     public void testInitialChangeListenerEventWithNestedWildcardedListsPath() throws Exception {
107         mergeToStore(shard.getDataStore(), TEST_PATH, testNodeWithOuter(outerNode(
108                 outerNodeEntry(1, innerNode("one", "two")), outerNodeEntry(2, innerNode("three", "four")))));
109
110         MockDataChangeListener listener = registerChangeListener(
111                 OUTER_LIST_PATH.node(OUTER_LIST_QNAME).node(INNER_LIST_QNAME).node(INNER_LIST_QNAME),
112                 DataChangeScope.ONE, 1, true);
113
114         listener.waitForChangeEvents();
115         listener.verifyCreatedData(0, innerEntryPath(1, "one"));
116         listener.verifyCreatedData(0, innerEntryPath(1, "two"));
117         listener.verifyCreatedData(0, innerEntryPath(2, "three"));
118         listener.verifyCreatedData(0, innerEntryPath(2, "four"));
119
120         // Register for a specific outer list entry
121
122         MockDataChangeListener listener2 = registerChangeListener(
123                 OUTER_LIST_PATH.node(outerEntryKey(1)).node(INNER_LIST_QNAME).node(INNER_LIST_QNAME),
124                 DataChangeScope.ONE, 1, true);
125
126         listener2.waitForChangeEvents();
127         listener2.verifyCreatedData(0, innerEntryPath(1, "one"));
128         listener2.verifyCreatedData(0, innerEntryPath(1, "two"));
129         listener2.verifyNoCreatedData(0, innerEntryPath(2, "three"));
130         listener2.verifyNoCreatedData(0, innerEntryPath(2, "four"));
131     }
132
133     @Test
134     public void testInitialChangeListenerEventWhenNotInitiallyLeader() throws Exception {
135         mergeToStore(shard.getDataStore(), TEST_PATH, testNodeWithOuter(outerNode(
136                 outerNodeEntry(1, innerNode("one", "two")), outerNodeEntry(2, innerNode("three", "four")))));
137
138         MockDataChangeListener listener = registerChangeListener(
139                 OUTER_LIST_PATH.node(OUTER_LIST_QNAME).node(INNER_LIST_QNAME).node(INNER_LIST_QNAME),
140                 DataChangeScope.ONE, 0, false);
141
142         listener.expectNoMoreChanges("Unexpected initial change event");
143         listener.reset(1);
144
145         support.onLeadershipChange(true, true);
146
147         listener.waitForChangeEvents();
148         listener.verifyCreatedData(0, innerEntryPath(1, "one"));
149         listener.verifyCreatedData(0, innerEntryPath(1, "two"));
150         listener.verifyCreatedData(0, innerEntryPath(2, "three"));
151         listener.verifyCreatedData(0, innerEntryPath(2, "four"));
152     }
153
154     private MockDataChangeListener registerChangeListener(final YangInstanceIdentifier path, final DataChangeScope scope,
155             final int expectedEvents, final boolean isLeader) {
156         MockDataChangeListener listener = new MockDataChangeListener(expectedEvents);
157         ActorRef dclActor = actorFactory.createActor(DataChangeListener.props(listener));
158
159         support.onMessage(new RegisterChangeListener(path, dclActor, scope, false), isLeader, true);
160         return listener;
161     }
162
163     private Shard createShard() {
164         TestActorRef<Shard> actor = actorFactory.createTestActor(newShardProps());
165         ShardTestKit.waitUntilLeader(actor);
166
167         return actor.underlyingActor();
168     }
169 }