BUG 5697 : Consumer support in shards
[mdsal.git] / dom / mdsal-dom-broker / src / test / java / org / opendaylight / mdsal / dom / broker / test / ShardedDOMDataTreeProducerMultiShardTest.java
index f8d4b249d19db6d7d1a4b5dc867b978d9a66a446..c108d1446ddfaddc800b65446c8cade058e125e4 100644 (file)
@@ -1,18 +1,34 @@
 package org.opendaylight.mdsal.dom.broker.test;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyCollection;
+import static org.mockito.Matchers.anyMap;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import com.google.common.util.concurrent.Futures;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.InOrder;
 import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeListener;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteCursor;
 import org.opendaylight.mdsal.dom.broker.ShardedDOMDataTree;
 import org.opendaylight.mdsal.dom.broker.test.util.TestModel;
@@ -27,89 +43,132 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgum
 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class ShardedDOMDataTreeProducerMultiShardTest {
 
-    private SchemaContext schemaContext;
+    private static final Logger LOG = LoggerFactory.getLogger(ShardedDOMDataTreeProducerMultiShardTest.class);
 
-    private static final DOMDataTreeIdentifier ROOT_ID = new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL,
-            YangInstanceIdentifier.EMPTY);
-    private static final DOMDataTreeIdentifier TEST_ID = new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL,
-            TestModel.TEST_PATH);
+    private static SchemaContext schemaContext = null;
 
-    private static final DOMDataTreeIdentifier TEST2_ID = new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL,
-            TestModel.TEST2_PATH);
+    static {
+        try {
+            schemaContext = TestModel.createTestContext();
+        } catch (ReactorException e) {
+            LOG.error("Unable to create schema context for TestModel", e);
+        }
+    }
 
-    private static final DOMDataTreeIdentifier INNER_CONTAINER_ID = new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.INNER_CONTAINER_PATH);
-    private static final DOMDataTreeIdentifier ANOTHER_SHARD_ID = new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.ANOTHER_SHARD_PATH);
+    private static final DOMDataTreeIdentifier ROOT_ID =
+            new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, YangInstanceIdentifier.EMPTY);
+    private static final DOMDataTreeIdentifier TEST_ID =
+            new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.TEST_PATH);
 
-    private InMemoryDOMDataTreeShard rootShard;
+    private static final DOMDataTreeIdentifier TEST2_ID =
+            new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.TEST2_PATH);
 
-    private InMemoryDOMDataTreeShard anotherInnerShard;
+    private static final DOMDataTreeIdentifier INNER_CONTAINER_ID =
+            new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.INNER_CONTAINER_PATH);
+    private static final DOMDataTreeIdentifier ANOTHER_SHARD_ID =
+            new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, TestModel.ANOTHER_SHARD_PATH);
 
+    private InMemoryDOMDataTreeShard rootShard;
+    private InMemoryDOMDataTreeShard anotherInnerShard;
 
     private ShardedDOMDataTree dataTreeService;
     private ListenerRegistration<InMemoryDOMDataTreeShard> rootShardReg;
     private ListenerRegistration<InMemoryDOMDataTreeShard> innerShardReg;
 
+    private final ExecutorService executor = Executors.newSingleThreadExecutor();
+
+    @Captor
+    private ArgumentCaptor<Collection<DataTreeCandidate>> captorForChanges;
+    @Captor
+    private ArgumentCaptor<Map<DOMDataTreeIdentifier, NormalizedNode<?, ?>>> captorForSubtrees;
+
+    private final ContainerNode crossShardContainer = createCrossShardContainer();
+
     @Before
     public void setUp() throws Exception {
-        schemaContext = TestModel.createTestContext();
+        MockitoAnnotations.initMocks(this);
 
-        rootShard = InMemoryDOMDataTreeShard.create(ROOT_ID);
+        rootShard = InMemoryDOMDataTreeShard.create(ROOT_ID, executor, 5000);
         rootShard.onGlobalContextUpdated(schemaContext);
 
-        ShardedDOMDataTree dataTree = new ShardedDOMDataTree();
+        final ShardedDOMDataTree dataTree = new ShardedDOMDataTree();
         rootShardReg = dataTree.registerDataTreeShard(ROOT_ID, rootShard);
 
         dataTreeService = dataTree;
     }
 
+    @Test
+    public void testSingleShardListener() throws Exception {
+        final DOMDataTreeListener mockedDataTreeListener = Mockito.mock(DOMDataTreeListener.class);
+        doNothing().when(mockedDataTreeListener).onDataTreeChanged(anyCollection(), anyMap());
+
+        dataTreeService.registerListener(mockedDataTreeListener, Collections.singletonList(INNER_CONTAINER_ID), true, Collections.emptyList());
+
+        final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
+        final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
+        writeCrossShardContainer(transaction);
+
+        verify(mockedDataTreeListener, timeout(1000)).onDataTreeChanged(captorForChanges.capture(), captorForSubtrees.capture());
+        final Collection<DataTreeCandidate> capturedValue = captorForChanges.getValue();
+        assertTrue(capturedValue.size() == 1);
+
+        final ContainerNode dataAfter = (ContainerNode) capturedValue.iterator().next().getRootNode().getDataAfter().get();
+        assertEquals(crossShardContainer.getChild(TestModel.INNER_CONTAINER_PATH.getLastPathArgument()).get(), dataAfter);
+
+        final Map<DOMDataTreeIdentifier, NormalizedNode<?, ?>> capturedSubtrees = captorForSubtrees.getValue();
+        assertTrue(capturedSubtrees.size() == 1);
+        assertTrue(capturedSubtrees.containsKey(INNER_CONTAINER_ID));
+        assertEquals(crossShardContainer.getChild(TestModel.INNER_CONTAINER_PATH.getLastPathArgument()).get(), capturedSubtrees.get(INNER_CONTAINER_ID));
+
+        verifyNoMoreInteractions(mockedDataTreeListener);
+    }
+
     @Test
     public void testMultipleShards() throws Exception {
-        //FIXME after listeners are implemented add them here and test those
+        final DOMDataTreeListener mockedDataTreeListener = Mockito.mock(DOMDataTreeListener.class);
+        doNothing().when(mockedDataTreeListener).onDataTreeChanged(anyCollection(), anyMap());
 
-        final InMemoryDOMDataTreeShard innerShard = InMemoryDOMDataTreeShard.create(INNER_CONTAINER_ID);
+        final InMemoryDOMDataTreeShard innerShard = InMemoryDOMDataTreeShard.create(INNER_CONTAINER_ID, executor, 5000);
         innerShard.onGlobalContextUpdated(schemaContext);
         innerShardReg = dataTreeService.registerDataTreeShard(INNER_CONTAINER_ID, innerShard);
 
+        dataTreeService.registerListener(mockedDataTreeListener, Collections.singletonList(TEST_ID), true, Collections.emptyList());
+
         final DOMDataTreeShardProducer producer = rootShard.createProducer(Collections.singletonList(TEST_ID));
         final DOMDataTreeShardWriteTransaction transaction = producer.createTransaction();
-        final DOMDataTreeWriteCursor cursor = transaction.createCursor(ROOT_ID);
-        cursor.enter(TestModel.TEST_PATH.getLastPathArgument());
+        writeCrossShardContainer(transaction);
 
-        final LeafNode<String> shardedValue1 =
-                ImmutableLeafNodeBuilder.<String>create().withNodeIdentifier(new NodeIdentifier(TestModel.SHARDED_VALUE_1)).withValue("sharded value 1").build();
-        final LeafNode<String> shardedValue2 =
-                ImmutableLeafNodeBuilder.<String>create().withNodeIdentifier(new NodeIdentifier(TestModel.SHARDED_VALUE_2)).withValue("sharded value 2").build();
+        final ContainerNode testContainerVerificationNode = ImmutableContainerNodeBuilder.create()
+                .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
+                .build();
 
-        final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> containerNodeBuilder = ImmutableContainerNodeBuilder.create();
-        final ContainerNode containerNode =
-                containerNodeBuilder
-                        .withNodeIdentifier(new NodeIdentifier(TestModel.INNER_CONTAINER))
-                        .withChild(shardedValue1)
-                        .withChild(shardedValue2)
-                        .build();
+        //verify listeners have been notified
+        verify(mockedDataTreeListener, timeout(1000).times(2)).onDataTreeChanged(captorForChanges.capture(), captorForSubtrees.capture());
+        final List<Collection<DataTreeCandidate>> capturedChanges = captorForChanges.getAllValues();
+        final List<Map<DOMDataTreeIdentifier, NormalizedNode<?, ?>>> capturedSubtrees = captorForSubtrees.getAllValues();
+        final DataTreeCandidate firstNotificationCandidate = capturedChanges.get(0).iterator().next();
 
-        cursor.write(TestModel.INNER_CONTAINER_PATH.getLastPathArgument(), containerNode);
-        cursor.enter(TestModel.INNER_CONTAINER_PATH.getLastPathArgument());
+        assertTrue(capturedSubtrees.get(0).size() == 1);
+        assertEquals(testContainerVerificationNode, firstNotificationCandidate.getRootNode().getDataAfter().get());
+        assertEquals(testContainerVerificationNode, capturedSubtrees.get(0).get(TEST_ID));
 
-        final ContainerNode lowerShardContainer = ImmutableContainerNodeBuilder.create()
-                .withNodeIdentifier(new NodeIdentifier(TestModel.ANOTHER_SHARD_CONTAINER))
-                .withChild(ImmutableLeafNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(TestModel.ANOTHER_SHARD_VALUE)).build())
-                .build();
-
-        cursor.write(TestModel.ANOTHER_SHARD_PATH.getLastPathArgument(), lowerShardContainer);
-        cursor.close();
-        transaction.ready();
-        transaction.submit().get();
+        final DataTreeCandidate secondNotificationCandidate = capturedChanges.get(1).iterator().next();
+        assertTrue(capturedSubtrees.get(1).size() == 1);
+        assertEquals(crossShardContainer, secondNotificationCandidate.getRootNode().getDataAfter().get());
+        assertEquals(crossShardContainer, capturedSubtrees.get(1).get(TEST_ID));
 
-        //verify listeners have been notified
+        verifyNoMoreInteractions(mockedDataTreeListener);
     }
 
     @Test
@@ -164,10 +223,52 @@ public class ShardedDOMDataTreeProducerMultiShardTest {
         transaction.ready();
         transaction.submit().get();
 
-        verify(mockedTx).ready();
-        verify(mockedTx).validate();
-        verify(mockedTx).prepare();
-        verify(mockedTx).commit();
+        final InOrder inOrder = inOrder(mockedTx);
+        inOrder.verify(mockedTx).ready();
+        inOrder.verify(mockedTx).validate();
+        inOrder.verify(mockedTx).prepare();
+        inOrder.verify(mockedTx).commit();
 
     }
+
+    private ContainerNode createCrossShardContainer() {
+        final LeafNode<String> shardedValue1 =
+                ImmutableLeafNodeBuilder.<String>create().withNodeIdentifier(new NodeIdentifier(TestModel.SHARDED_VALUE_1)).withValue("sharded value 1").build();
+        final LeafNode<String> shardedValue2 =
+                ImmutableLeafNodeBuilder.<String>create().withNodeIdentifier(new NodeIdentifier(TestModel.SHARDED_VALUE_2)).withValue("sharded value 2").build();
+
+
+        final ContainerNode lowerShardContainer = ImmutableContainerNodeBuilder.create()
+                .withNodeIdentifier(new NodeIdentifier(TestModel.ANOTHER_SHARD_CONTAINER))
+                .withChild(ImmutableLeafNodeBuilder.create()
+                        .withNodeIdentifier(new NodeIdentifier(TestModel.ANOTHER_SHARD_VALUE))
+                        .withValue("testing-value")
+                        .build())
+                .build();
+
+        final ContainerNode containerNode =
+                ImmutableContainerNodeBuilder.create()
+                        .withNodeIdentifier(new NodeIdentifier(TestModel.INNER_CONTAINER))
+                        .withChild(shardedValue1)
+                        .withChild(shardedValue2)
+                        .withChild(lowerShardContainer)
+                        .build();
+
+        final ContainerNode testContainer = ImmutableContainerNodeBuilder.create()
+                .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
+                .withChild(containerNode)
+                .build();
+
+        return testContainer;
+    }
+
+    private void writeCrossShardContainer(final DOMDataTreeShardWriteTransaction transaction) throws Exception{
+        final DOMDataTreeWriteCursor cursor = transaction.createCursor(ROOT_ID);
+
+        cursor.write(TestModel.TEST_PATH.getLastPathArgument(), crossShardContainer);
+
+        cursor.close();
+        transaction.ready();
+        transaction.submit().get();
+    }
 }