Make AbstractDOMShardTreeChangePublisher advertise initial data change
[mdsal.git] / dom / mdsal-dom-broker / src / test / java / org / opendaylight / mdsal / dom / broker / ShardedDOMDataTreeProducerMultiShardTest.java
1 /*
2  * Copyright (c) 2016 Cisco 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.mdsal.dom.broker;
9
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertTrue;
12 import static org.mockito.Matchers.any;
13 import static org.mockito.Matchers.anyCollection;
14 import static org.mockito.Matchers.anyMap;
15 import static org.mockito.Mockito.doNothing;
16 import static org.mockito.Mockito.doReturn;
17 import static org.mockito.Mockito.inOrder;
18 import static org.mockito.Mockito.timeout;
19 import static org.mockito.Mockito.verify;
20 import static org.mockito.Mockito.verifyNoMoreInteractions;
21
22 import com.google.common.util.concurrent.Futures;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.concurrent.ExecutorService;
28 import java.util.concurrent.Executors;
29 import org.junit.Before;
30 import org.junit.Test;
31 import org.mockito.ArgumentCaptor;
32 import org.mockito.Captor;
33 import org.mockito.InOrder;
34 import org.mockito.Mockito;
35 import org.mockito.MockitoAnnotations;
36 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
37 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
38 import org.opendaylight.mdsal.dom.api.DOMDataTreeListener;
39 import org.opendaylight.mdsal.dom.api.DOMDataTreeProducer;
40 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteCursor;
41 import org.opendaylight.mdsal.dom.broker.util.TestModel;
42 import org.opendaylight.mdsal.dom.store.inmemory.DOMDataTreeShardProducer;
43 import org.opendaylight.mdsal.dom.store.inmemory.DOMDataTreeShardWriteTransaction;
44 import org.opendaylight.mdsal.dom.store.inmemory.InMemoryDOMDataTreeShard;
45 import org.opendaylight.mdsal.dom.store.inmemory.WriteableDOMDataTreeShard;
46 import org.opendaylight.yangtools.concepts.ListenerRegistration;
47 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
48 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
49 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
50 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
51 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
52 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
53 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
54 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
55 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
56 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder;
57 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
58 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
61
62 public class ShardedDOMDataTreeProducerMultiShardTest {
63
64     private static final Logger LOG = LoggerFactory.getLogger(ShardedDOMDataTreeProducerMultiShardTest.class);
65
66     private static SchemaContext schemaContext = null;
67
68     static {
69         try {
70             schemaContext = TestModel.createTestContext();
71         } catch (final ReactorException e) {
72             LOG.error("Unable to create schema context for TestModel", e);
73         }
74     }
75
76     private static final DOMDataTreeIdentifier ROOT_ID =
77             new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.EMPTY);
78     private static final DOMDataTreeIdentifier TEST_ID =
79             new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.TEST_PATH);
80
81     private static final DOMDataTreeIdentifier TEST2_ID =
82             new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.TEST2_PATH);
83
84     private static final DOMDataTreeIdentifier INNER_CONTAINER_ID =
85             new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.INNER_CONTAINER_PATH);
86     private static final DOMDataTreeIdentifier ANOTHER_SHARD_ID =
87             new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.ANOTHER_SHARD_PATH);
88
89     private InMemoryDOMDataTreeShard rootShard;
90     private InMemoryDOMDataTreeShard anotherInnerShard;
91
92     private ShardedDOMDataTree dataTreeService;
93     private ListenerRegistration<InMemoryDOMDataTreeShard> rootShardReg;
94     private ListenerRegistration<InMemoryDOMDataTreeShard> innerShardReg;
95
96     private final ExecutorService executor = Executors.newSingleThreadExecutor();
97
98     @Captor
99     private ArgumentCaptor<Collection<DataTreeCandidate>> captorForChanges;
100     @Captor
101     private ArgumentCaptor<Map<DOMDataTreeIdentifier, NormalizedNode<?, ?>>> captorForSubtrees;
102
103     private final ContainerNode crossShardContainer = createCrossShardContainer();
104
105     @Before
106     public void setUp() throws Exception {
107         MockitoAnnotations.initMocks(this);
108
109         rootShard = InMemoryDOMDataTreeShard.create(ROOT_ID, executor, 1, 1);
110         rootShard.onGlobalContextUpdated(schemaContext);
111
112         final ShardedDOMDataTree dataTree = new ShardedDOMDataTree();
113         final DOMDataTreeProducer shardRegProducer = dataTree.createProducer(Collections.singletonList(ROOT_ID));
114         rootShardReg = dataTree.registerDataTreeShard(ROOT_ID, rootShard, shardRegProducer);
115         shardRegProducer.close();
116
117         dataTreeService = dataTree;
118     }
119
120     @Test(expected=IllegalStateException.class)
121     public void testTxReadyMultiples() throws Exception {
122         final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
123         final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
124         final DOMDataTreeWriteCursor cursor = transaction.createCursor(ROOT_ID);
125
126         final ContainerNode testContainer = ImmutableContainerNodeBuilder.create()
127                 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
128                 .build();
129         cursor.write(TestModel.TEST_PATH.getLastPathArgument(), testContainer);
130         transaction.ready();
131
132         transaction.ready();
133     }
134
135     @Test(expected=IllegalStateException.class)
136     public void testSubmitUnclosedCursor() throws Exception {
137         final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
138         final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
139         final DOMDataTreeWriteCursor cursor = transaction.createCursor(ROOT_ID);
140
141         final ContainerNode testContainer = ImmutableContainerNodeBuilder.create()
142                 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
143                 .build();
144
145         cursor.write(TestModel.TEST_PATH.getLastPathArgument(), testContainer);
146         transaction.ready();
147     }
148
149     @Test
150     public void testMultipleCursorsFromOneTx() throws Exception {
151         final DOMDataTreeListener mockedDataTreeListener = Mockito.mock(DOMDataTreeListener.class);
152         doNothing().when(mockedDataTreeListener).onDataTreeChanged(anyCollection(), anyMap());
153
154         dataTreeService.registerListener(mockedDataTreeListener, Collections.singletonList(INNER_CONTAINER_ID), true, Collections.emptyList());
155
156         final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
157         final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
158         final DOMDataTreeWriteCursor cursor = transaction.createCursor(ROOT_ID);
159
160         final ContainerNode testContainer = ImmutableContainerNodeBuilder.create()
161                 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
162                 .build();
163
164         cursor.write(TestModel.TEST_PATH.getLastPathArgument(), testContainer);
165         cursor.close();
166
167         final DOMDataTreeWriteCursor newCursor = transaction.createCursor(ROOT_ID);
168         newCursor.enter(TestModel.TEST_PATH.getLastPathArgument());
169         final ContainerNode innerContainer = ImmutableContainerNodeBuilder.create()
170                 .withNodeIdentifier(new NodeIdentifier(TestModel.INNER_CONTAINER))
171                 .withChild(ImmutableLeafNodeBuilder.<String>create()
172                         .withNodeIdentifier(new NodeIdentifier(TestModel.SHARDED_VALUE_1))
173                         .withValue("inner-value")
174                         .build())
175                 .build();
176
177         newCursor.write(TestModel.INNER_CONTAINER_PATH.getLastPathArgument(), innerContainer);
178         newCursor.close();
179         transaction.ready();
180         transaction.submit();
181
182         verify(mockedDataTreeListener, timeout(1000).times(2)).onDataTreeChanged(captorForChanges.capture(), captorForSubtrees.capture());
183         final Collection<DataTreeCandidate> capturedValue = captorForChanges.getValue();
184         assertTrue(capturedValue.size() == 1);
185
186         final ContainerNode dataAfter = (ContainerNode) capturedValue.iterator().next().getRootNode().getDataAfter().get();
187         assertEquals(innerContainer, dataAfter);
188         verifyNoMoreInteractions(mockedDataTreeListener);
189     }
190
191     @Test
192     public void testSingleShardListener() throws Exception {
193         final DOMDataTreeListener mockedDataTreeListener = Mockito.mock(DOMDataTreeListener.class);
194         doNothing().when(mockedDataTreeListener).onDataTreeChanged(anyCollection(), anyMap());
195
196         dataTreeService.registerListener(mockedDataTreeListener, Collections.singletonList(INNER_CONTAINER_ID), true, Collections.emptyList());
197
198         final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
199         final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
200         writeCrossShardContainer(transaction);
201
202         verify(mockedDataTreeListener, timeout(1000).times(2)).onDataTreeChanged(captorForChanges.capture(), captorForSubtrees.capture());
203         final Collection<DataTreeCandidate> capturedValue = captorForChanges.getValue();
204         assertTrue(capturedValue.size() == 1);
205
206         final ContainerNode dataAfter = (ContainerNode) capturedValue.iterator().next().getRootNode().getDataAfter().get();
207         assertEquals(crossShardContainer.getChild(TestModel.INNER_CONTAINER_PATH.getLastPathArgument()).get(), dataAfter);
208
209         final Map<DOMDataTreeIdentifier, NormalizedNode<?, ?>> capturedSubtrees = captorForSubtrees.getValue();
210         assertTrue(capturedSubtrees.size() == 1);
211         assertTrue(capturedSubtrees.containsKey(INNER_CONTAINER_ID));
212         assertEquals(crossShardContainer.getChild(TestModel.INNER_CONTAINER_PATH.getLastPathArgument()).get(), capturedSubtrees.get(INNER_CONTAINER_ID));
213
214         verifyNoMoreInteractions(mockedDataTreeListener);
215     }
216
217     @Test
218     public void testMultipleShards() throws Exception {
219         final DOMDataTreeListener mockedDataTreeListener = Mockito.mock(DOMDataTreeListener.class);
220         doNothing().when(mockedDataTreeListener).onDataTreeChanged(anyCollection(), anyMap());
221
222         final InMemoryDOMDataTreeShard innerShard = InMemoryDOMDataTreeShard.create(INNER_CONTAINER_ID, executor, 1, 1);
223         innerShard.onGlobalContextUpdated(schemaContext);
224         final DOMDataTreeProducer shardRegProducer = dataTreeService.createProducer(Collections.singletonList(INNER_CONTAINER_ID));
225         innerShardReg = dataTreeService.registerDataTreeShard(INNER_CONTAINER_ID, innerShard, shardRegProducer);
226         shardRegProducer.close();
227
228         dataTreeService.registerListener(mockedDataTreeListener, Collections.singletonList(TEST_ID), true, Collections.emptyList());
229
230         final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
231         final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
232         writeCrossShardContainer(transaction);
233
234         final ContainerNode testContainerVerificationNode = ImmutableContainerNodeBuilder.create()
235                 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
236                 .build();
237
238         //verify listeners have been notified
239         verify(mockedDataTreeListener, timeout(1000).times(4)).onDataTreeChanged(captorForChanges.capture(), captorForSubtrees.capture());
240         final List<Collection<DataTreeCandidate>> capturedChanges = captorForChanges.getAllValues();
241         final List<Map<DOMDataTreeIdentifier, NormalizedNode<?, ?>>> capturedSubtrees = captorForSubtrees.getAllValues();
242         final DataTreeCandidate firstNotificationCandidate = capturedChanges.get(2).iterator().next();
243
244         assertTrue(capturedSubtrees.get(2).size() == 1);
245         assertEquals(testContainerVerificationNode, firstNotificationCandidate.getRootNode().getDataAfter().get());
246         assertEquals(testContainerVerificationNode, capturedSubtrees.get(2).get(TEST_ID));
247
248         final DataTreeCandidate secondNotificationCandidate = capturedChanges.get(3).iterator().next();
249         assertTrue(capturedSubtrees.get(3).size() == 1);
250         assertEquals(crossShardContainer, secondNotificationCandidate.getRootNode().getDataAfter().get());
251         assertEquals(crossShardContainer, capturedSubtrees.get(3).get(TEST_ID));
252
253         verifyNoMoreInteractions(mockedDataTreeListener);
254     }
255
256     @Test
257     public void testMultipleWritesIntoSingleShard() throws Exception {
258         final DOMDataTreeListener mockedDataTreeListener = Mockito.mock(DOMDataTreeListener.class);
259         doNothing().when(mockedDataTreeListener).onDataTreeChanged(anyCollection(), anyMap());
260
261         dataTreeService.registerListener(mockedDataTreeListener, Collections.singletonList(INNER_CONTAINER_ID), true, Collections.emptyList());
262
263         final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
264         final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
265         writeCrossShardContainer(transaction);
266
267         final DOMDataTreeShardWriteTransaction newTx = producer.createTransaction();
268         final DOMDataTreeWriteCursor newCursor = newTx.createCursor(ROOT_ID);
269
270         newCursor.delete(TestModel.TEST_PATH.getLastPathArgument());
271     }
272
273     @Test
274     public void testMockedSubshards() throws Exception {
275         final WriteableDOMDataTreeShard mockedInnerShard = Mockito.mock(WriteableDOMDataTreeShard.class);
276         final ShardedDOMDataTreeProducer shardRegProducer = Mockito.mock(ShardedDOMDataTreeProducer.class);
277         doReturn(Collections.singleton(INNER_CONTAINER_ID)).when(shardRegProducer).getSubtrees();
278         doNothing().when(shardRegProducer).subshardAdded(anyMap());
279
280         dataTreeService.registerDataTreeShard(INNER_CONTAINER_ID, mockedInnerShard, shardRegProducer);
281         final DOMDataTreeShardProducer mockedProducer = Mockito.mock(DOMDataTreeShardProducer.class);
282         doReturn(mockedProducer).when(mockedInnerShard).createProducer(any(Collection.class));
283
284         final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
285
286         final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
287         final DOMDataTreeWriteCursor cursor = transaction.createCursor(ROOT_ID);
288         cursor.enter(TestModel.TEST_PATH.getLastPathArgument());
289
290         final LeafNode<String> shardedValue1 =
291                 ImmutableLeafNodeBuilder.<String>create().withNodeIdentifier(new NodeIdentifier(TestModel.SHARDED_VALUE_1)).withValue("sharded value 1").build();
292         final LeafNode<String> shardedValue2 =
293                 ImmutableLeafNodeBuilder.<String>create().withNodeIdentifier(new NodeIdentifier(TestModel.SHARDED_VALUE_2)).withValue("sharded value 2").build();
294
295         final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> containerNodeBuilder = ImmutableContainerNodeBuilder.create();
296         final ContainerNode containerNode =
297                 containerNodeBuilder
298                         .withNodeIdentifier(new NodeIdentifier(TestModel.INNER_CONTAINER))
299                         .withChild(shardedValue1)
300                         .withChild(shardedValue2)
301                         .build();
302
303         final DOMDataTreeShardWriteTransaction mockedTx = Mockito.mock(DOMDataTreeShardWriteTransaction.class);
304         doReturn(mockedTx).when(mockedProducer).createTransaction();
305
306         doNothing().when(mockedTx).ready();
307         doReturn(Futures.immediateFuture(true)).when(mockedTx).validate();
308         doReturn(Futures.immediateFuture(null)).when(mockedTx).prepare();
309         doReturn(Futures.immediateFuture(null)).when(mockedTx).commit();
310
311         final DOMDataTreeWriteCursor mockedCursor = Mockito.mock(DOMDataTreeWriteCursor.class);
312         doNothing().when(mockedCursor).write(any(PathArgument.class), any(NormalizedNode.class));
313         doNothing().when(mockedCursor).close();
314         doReturn(mockedCursor).when(mockedTx).createCursor(any(DOMDataTreeIdentifier.class));
315
316         cursor.write(TestModel.INNER_CONTAINER_PATH.getLastPathArgument(), containerNode);
317         cursor.enter(TestModel.INNER_CONTAINER_PATH.getLastPathArgument());
318
319         final ContainerNode lowerShardContainer = ImmutableContainerNodeBuilder.create()
320                 .withNodeIdentifier(new NodeIdentifier(TestModel.ANOTHER_SHARD_CONTAINER))
321                 .withChild(ImmutableLeafNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(TestModel.ANOTHER_SHARD_VALUE)).build())
322                 .build();
323
324         cursor.write(TestModel.ANOTHER_SHARD_PATH.getLastPathArgument(), lowerShardContainer);
325         cursor.close();
326         transaction.ready();
327         transaction.submit().get();
328
329         final InOrder inOrder = inOrder(mockedTx);
330         inOrder.verify(mockedTx).ready();
331         inOrder.verify(mockedTx).validate();
332         inOrder.verify(mockedTx).prepare();
333         inOrder.verify(mockedTx).commit();
334
335     }
336
337     private ContainerNode createCrossShardContainer() {
338         final LeafNode<String> shardedValue1 =
339                 ImmutableLeafNodeBuilder.<String>create().withNodeIdentifier(new NodeIdentifier(TestModel.SHARDED_VALUE_1)).withValue("sharded value 1").build();
340         final LeafNode<String> shardedValue2 =
341                 ImmutableLeafNodeBuilder.<String>create().withNodeIdentifier(new NodeIdentifier(TestModel.SHARDED_VALUE_2)).withValue("sharded value 2").build();
342
343
344         final ContainerNode lowerShardContainer = ImmutableContainerNodeBuilder.create()
345                 .withNodeIdentifier(new NodeIdentifier(TestModel.ANOTHER_SHARD_CONTAINER))
346                 .withChild(ImmutableLeafNodeBuilder.create()
347                         .withNodeIdentifier(new NodeIdentifier(TestModel.ANOTHER_SHARD_VALUE))
348                         .withValue("testing-value")
349                         .build())
350                 .build();
351
352         final ContainerNode containerNode =
353                 ImmutableContainerNodeBuilder.create()
354                         .withNodeIdentifier(new NodeIdentifier(TestModel.INNER_CONTAINER))
355                         .withChild(shardedValue1)
356                         .withChild(shardedValue2)
357                         .withChild(lowerShardContainer)
358                         .build();
359
360         final ContainerNode testContainer = ImmutableContainerNodeBuilder.create()
361                 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
362                 .withChild(containerNode)
363                 .build();
364
365         return testContainer;
366     }
367
368     private void writeCrossShardContainer(final DOMDataTreeShardWriteTransaction transaction) throws Exception{
369         final DOMDataTreeWriteCursor cursor = transaction.createCursor(ROOT_ID);
370
371         cursor.write(TestModel.TEST_PATH.getLastPathArgument(), crossShardContainer);
372
373         cursor.close();
374         transaction.ready();
375         transaction.submit().get();
376     }
377 }