BUG 6057: Rewrite ShardedDOMProducer to use new cursor api
[mdsal.git] / dom / mdsal-dom-broker / src / test / java / org / opendaylight / mdsal / dom / broker / test / ShardedDOMDataTreeTest.java
1 package org.opendaylight.mdsal.dom.broker.test;
2
3 import static org.junit.Assert.assertEquals;
4 import static org.junit.Assert.assertNotNull;
5 import static org.junit.Assert.assertTrue;
6 import static org.junit.Assert.fail;
7 import static org.mockito.Matchers.anyCollection;
8 import static org.mockito.Matchers.anyMap;
9 import static org.mockito.Mockito.doNothing;
10 import static org.mockito.Mockito.timeout;
11 import static org.mockito.Mockito.verify;
12 import static org.mockito.Mockito.verifyNoMoreInteractions;
13
14 import java.util.Collection;
15 import java.util.Collections;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.concurrent.ExecutorService;
19 import java.util.concurrent.Executors;
20 import org.junit.Before;
21 import org.junit.Test;
22 import org.mockito.ArgumentCaptor;
23 import org.mockito.Captor;
24 import org.mockito.Mockito;
25 import org.mockito.MockitoAnnotations;
26 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
27 import org.opendaylight.mdsal.dom.api.DOMDataTreeCursorAwareTransaction;
28 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
29 import org.opendaylight.mdsal.dom.api.DOMDataTreeListener;
30 import org.opendaylight.mdsal.dom.api.DOMDataTreeProducer;
31 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteCursor;
32 import org.opendaylight.mdsal.dom.broker.ShardedDOMDataTree;
33 import org.opendaylight.mdsal.dom.broker.test.util.TestModel;
34 import org.opendaylight.mdsal.dom.store.inmemory.InMemoryDOMDataTreeShard;
35 import org.opendaylight.yangtools.concepts.ListenerRegistration;
36 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
37 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
38 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
39 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
40 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
41 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
42 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
43 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder;
44 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
45 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48
49 public class ShardedDOMDataTreeTest {
50
51     private static final Logger LOG = LoggerFactory.getLogger(ShardedDOMDataTreeProducerMultiShardTest.class);
52
53     private static SchemaContext schemaContext = null;
54
55     static {
56         try {
57             schemaContext = TestModel.createTestContext();
58         } catch (final ReactorException e) {
59             LOG.error("Unable to create schema context for TestModel", e);
60         }
61     }
62
63     private static final DOMDataTreeIdentifier ROOT_ID =
64             new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.EMPTY);
65     private static final DOMDataTreeIdentifier TEST_ID =
66             new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.TEST_PATH);
67
68     private static final DOMDataTreeIdentifier INNER_CONTAINER_ID =
69             new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.INNER_CONTAINER_PATH);
70
71     private InMemoryDOMDataTreeShard rootShard;
72
73     private ShardedDOMDataTree dataTreeService;
74     private ListenerRegistration<InMemoryDOMDataTreeShard> rootShardReg;
75
76     private final ExecutorService executor = Executors.newSingleThreadExecutor();
77
78     @Captor
79     private ArgumentCaptor<Collection<DataTreeCandidate>> captorForChanges;
80     @Captor
81     private ArgumentCaptor<Map<DOMDataTreeIdentifier, NormalizedNode<?, ?>>> captorForSubtrees;
82
83     private final ContainerNode crossShardContainer = createCrossShardContainer();
84
85     @Before
86     public void setUp() throws Exception {
87         MockitoAnnotations.initMocks(this);
88
89         rootShard = InMemoryDOMDataTreeShard.create(ROOT_ID, executor, 5000);
90         rootShard.onGlobalContextUpdated(schemaContext);
91
92         final ShardedDOMDataTree dataTree = new ShardedDOMDataTree();
93         rootShardReg = dataTree.registerDataTreeShard(ROOT_ID, rootShard);
94
95         dataTreeService = dataTree;
96     }
97
98     @Test(expected = IllegalArgumentException.class)
99     public void testProducerPathContention() throws Exception {
100         final DOMDataTreeProducer p1 = dataTreeService.createProducer(Collections.singletonList(ROOT_ID));
101         final DOMDataTreeProducer p2 = dataTreeService.createProducer(Collections.singletonList(TEST_ID));
102     }
103
104     @Test
105     public void testSingleShardWrite() throws Exception {
106         final DOMDataTreeListener mockedDataTreeListener = Mockito.mock(DOMDataTreeListener.class);
107         doNothing().when(mockedDataTreeListener).onDataTreeChanged(anyCollection(), anyMap());
108
109         dataTreeService.registerListener(mockedDataTreeListener, Collections.singletonList(INNER_CONTAINER_ID), true, Collections.emptyList());
110
111         final DOMDataTreeProducer producer = dataTreeService.createProducer(Collections.singletonList(ROOT_ID));
112         DOMDataTreeCursorAwareTransaction tx = producer.createTransaction(false);
113         DOMDataTreeWriteCursor cursor = tx.createCursor(ROOT_ID);
114         assertNotNull(cursor);
115
116         cursor.write(TEST_ID.getRootIdentifier().getLastPathArgument(), crossShardContainer);
117
118         try {
119             tx.submit().checkedGet();
120             fail("There's still an open cursor");
121         } catch (final IllegalStateException e) {
122             assertTrue(e.getMessage().contains("cursor open"));
123         }
124
125         cursor.close();
126         tx.submit().checkedGet();
127
128         tx = producer.createTransaction(false);
129         cursor = tx.createCursor(TEST_ID);
130         assertNotNull(cursor);
131
132         cursor.delete(TestModel.INNER_CONTAINER_PATH.getLastPathArgument());
133         cursor.close();
134         tx.submit().checkedGet();
135
136         verify(mockedDataTreeListener, timeout(1000).times(2)).onDataTreeChanged(captorForChanges.capture(), captorForSubtrees.capture());
137         final List<Collection<DataTreeCandidate>> capturedValue = captorForChanges.getAllValues();
138         assertTrue(capturedValue.size() == 2);
139
140         final ContainerNode capturedChange = (ContainerNode) capturedValue.get(0).iterator().next().getRootNode().getDataAfter().get();
141         final ContainerNode innerContainerVerify = (ContainerNode) crossShardContainer.getChild(TestModel.INNER_CONTAINER_PATH.getLastPathArgument()).get();
142         assertEquals(innerContainerVerify, capturedChange);
143
144         verifyNoMoreInteractions(mockedDataTreeListener);
145     }
146
147     @Test
148     public void testMultipleProducerCursorCreation() throws Exception {
149
150         final DOMDataTreeProducer rootProducer = dataTreeService.createProducer(Collections.singletonList(ROOT_ID));
151         DOMDataTreeCursorAwareTransaction rootTx = rootProducer.createTransaction(false);
152         //check if we can create cursor where the new producer will be
153         DOMDataTreeWriteCursor rootTxCursor = rootTx.createCursor(INNER_CONTAINER_ID);
154         assertNotNull(rootTxCursor);
155         rootTxCursor.close();
156
157         try {
158             rootProducer.createProducer(Collections.singletonList(INNER_CONTAINER_ID));
159             fail("Should've failed there is still a tx open");
160         } catch (final IllegalStateException e) {
161             assertTrue(e.getMessage().contains("open"));
162         }
163
164         assertTrue(rootTx.cancel());
165
166         final DOMDataTreeProducer innerContainerProducer = rootProducer.createProducer(Collections.singletonList(INNER_CONTAINER_ID));
167
168         rootTx = rootProducer.createTransaction(false);
169         try {
170             rootTx.createCursor(INNER_CONTAINER_ID);
171             fail("Subtree should not be available to this producer");
172         } catch (final IllegalArgumentException e) {
173             assertTrue(e.getMessage().contains("delegated to child producer"));
174         }
175
176         rootTxCursor = rootTx.createCursor(TEST_ID);
177         assertNotNull(rootTxCursor);
178         try {
179             rootTxCursor.enter(INNER_CONTAINER_ID.getRootIdentifier().getLastPathArgument());
180             fail("Cursor should not have this subtree available");
181         } catch (final IllegalArgumentException e) {
182             assertTrue(e.getMessage().contains("not available to this cursor"));
183         }
184
185         try {
186             rootTxCursor.write(TestModel.INNER_CONTAINER_PATH.getLastPathArgument(),
187                     ImmutableContainerNodeBuilder.create()
188                             .withNodeIdentifier(new NodeIdentifier(TestModel.INNER_CONTAINER))
189                             .build());
190             fail("Cursor should not have this subtree available");
191         } catch (final IllegalArgumentException e) {
192             assertTrue(e.getMessage().contains("not available to this cursor"));
193         }
194
195         final DOMDataTreeCursorAwareTransaction innerShardTx = innerContainerProducer.createTransaction(false);
196         final DOMDataTreeWriteCursor innerShardCursor = innerShardTx.createCursor(INNER_CONTAINER_ID);
197         assertNotNull(innerShardCursor);
198     }
199
200     private ContainerNode createCrossShardContainer() {
201         final LeafNode<String> shardedValue1 =
202                 ImmutableLeafNodeBuilder.<String>create().withNodeIdentifier(new NodeIdentifier(TestModel.SHARDED_VALUE_1)).withValue("sharded value 1").build();
203         final LeafNode<String> shardedValue2 =
204                 ImmutableLeafNodeBuilder.<String>create().withNodeIdentifier(new NodeIdentifier(TestModel.SHARDED_VALUE_2)).withValue("sharded value 2").build();
205
206
207         final ContainerNode lowerShardContainer = ImmutableContainerNodeBuilder.create()
208                 .withNodeIdentifier(new NodeIdentifier(TestModel.ANOTHER_SHARD_CONTAINER))
209                 .withChild(ImmutableLeafNodeBuilder.create()
210                         .withNodeIdentifier(new NodeIdentifier(TestModel.ANOTHER_SHARD_VALUE))
211                         .withValue("testing-value")
212                         .build())
213                 .build();
214
215         final ContainerNode containerNode =
216                 ImmutableContainerNodeBuilder.create()
217                         .withNodeIdentifier(new NodeIdentifier(TestModel.INNER_CONTAINER))
218                         .withChild(shardedValue1)
219                         .withChild(shardedValue2)
220                         .withChild(lowerShardContainer)
221                         .build();
222
223         final ContainerNode testContainer = ImmutableContainerNodeBuilder.create()
224                 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
225                 .withChild(containerNode)
226                 .build();
227
228         return testContainer;
229     }
230 }