Adjust to yangtools-2.0.0 changes
[mdsal.git] / dom / mdsal-dom-broker / src / test / java / org / opendaylight / mdsal / dom / broker / DOMDataTreeListenerTest.java
1 /*
2  * Copyright (c) 2015 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
9 package org.opendaylight.mdsal.dom.broker;
10
11 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertFalse;
13 import static org.junit.Assert.assertNotNull;
14 import static org.junit.Assert.assertTrue;
15 import static org.opendaylight.mdsal.common.api.LogicalDatastoreType.CONFIGURATION;
16 import static org.opendaylight.mdsal.common.api.LogicalDatastoreType.OPERATIONAL;
17
18 import com.google.common.collect.ImmutableMap;
19 import com.google.common.util.concurrent.ForwardingExecutorService;
20 import com.google.common.util.concurrent.ListeningExecutorService;
21 import com.google.common.util.concurrent.MoreExecutors;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.List;
25 import java.util.concurrent.CountDownLatch;
26 import java.util.concurrent.ExecutionException;
27 import java.util.concurrent.ExecutorService;
28 import java.util.concurrent.Executors;
29 import java.util.concurrent.TimeUnit;
30 import javax.annotation.Nonnull;
31 import org.junit.After;
32 import org.junit.Before;
33 import org.junit.Test;
34 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
35 import org.opendaylight.mdsal.common.api.TransactionCommitDeadlockException;
36 import org.opendaylight.mdsal.dom.api.DOMDataBrokerExtension;
37 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
38 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
39 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
40 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
41 import org.opendaylight.mdsal.dom.broker.util.TestModel;
42 import org.opendaylight.mdsal.dom.spi.store.DOMStore;
43 import org.opendaylight.mdsal.dom.store.inmemory.InMemoryDOMDataStore;
44 import org.opendaylight.yangtools.concepts.ListenerRegistration;
45 import org.opendaylight.yangtools.util.concurrent.DeadlockDetectingListeningExecutorService;
46 import org.opendaylight.yangtools.util.concurrent.SpecialExecutors;
47 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
48 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
49 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
50 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
51 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
52 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
53 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
54 import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
55 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
56 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
57 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
58
59 public class DOMDataTreeListenerTest {
60
61     private SchemaContext schemaContext;
62     private AbstractDOMDataBroker domBroker;
63     private ListeningExecutorService executor;
64     private ExecutorService futureExecutor;
65     private CommitExecutorService commitExecutor;
66
67     private static final DataContainerChild<?, ?> OUTER_LIST =
68             ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
69             .withChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1))
70             .build();
71
72     private static final DataContainerChild<?, ?> OUTER_LIST_2 =
73             ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
74             .withChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2))
75             .build();
76
77     private static final NormalizedNode<?, ?> TEST_CONTAINER = Builders.containerBuilder()
78             .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
79             .withChild(OUTER_LIST)
80             .build();
81
82     private static final NormalizedNode<?, ?> TEST_CONTAINER_2 = Builders.containerBuilder()
83             .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
84             .withChild(OUTER_LIST_2)
85             .build();
86
87     private static final DOMDataTreeIdentifier ROOT_DATA_TREE_ID = new DOMDataTreeIdentifier(
88             LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
89
90     private static final DOMDataTreeIdentifier OUTER_LIST_DATA_TREE_ID = new DOMDataTreeIdentifier(
91             LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH);
92
93     @Before
94     public void setupStore() {
95         final InMemoryDOMDataStore operStore = new InMemoryDOMDataStore("OPER",
96                 MoreExecutors.newDirectExecutorService());
97         final InMemoryDOMDataStore configStore = new InMemoryDOMDataStore("CFG",
98                 MoreExecutors.newDirectExecutorService());
99         schemaContext = TestModel.createTestContext();
100
101         operStore.onGlobalContextUpdated(schemaContext);
102         configStore.onGlobalContextUpdated(schemaContext);
103
104         final ImmutableMap<LogicalDatastoreType, DOMStore> stores = ImmutableMap.<LogicalDatastoreType,
105                 DOMStore>builder()
106                 .put(CONFIGURATION, configStore)
107                 .put(OPERATIONAL, operStore)
108                 .build();
109
110         commitExecutor = new CommitExecutorService(Executors.newSingleThreadExecutor());
111         futureExecutor = SpecialExecutors.newBlockingBoundedCachedThreadPool(1, 5, "FCB",
112                 DOMDataTreeListenerTest.class);
113         executor = new DeadlockDetectingListeningExecutorService(commitExecutor,
114                 TransactionCommitDeadlockException.DEADLOCK_EXCEPTION_SUPPLIER, futureExecutor);
115         domBroker = new SerializedDOMDataBroker(stores, executor);
116     }
117
118     @After
119     public void tearDown() {
120         if (executor != null) {
121             executor.shutdownNow();
122         }
123
124         if (futureExecutor != null) {
125             futureExecutor.shutdownNow();
126         }
127     }
128
129     @Test
130     public void writeContainerEmptyTreeTest() throws InterruptedException {
131         final CountDownLatch latch = new CountDownLatch(1);
132
133         final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
134         assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
135                 dataTreeChangeService);
136
137         final TestDataTreeListener listener = new TestDataTreeListener(latch);
138         final ListenerRegistration<TestDataTreeListener> listenerReg =
139                 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
140
141         final DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
142         writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
143         writeTx.submit();
144
145         latch.await(5, TimeUnit.SECONDS);
146
147         assertEquals(1, listener.getReceivedChanges().size());
148         final Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
149         assertEquals(1, changes.size());
150
151         final DataTreeCandidate candidate = changes.iterator().next();
152         assertNotNull(candidate);
153         final DataTreeCandidateNode candidateRoot = candidate.getRootNode();
154         checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
155         listenerReg.close();
156     }
157
158     @Test
159     public void replaceContainerContainerInTreeTest() throws ExecutionException, InterruptedException {
160         final CountDownLatch latch = new CountDownLatch(2);
161
162         final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
163         assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
164                 dataTreeChangeService);
165
166         DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
167         writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
168         writeTx.submit().get();
169
170         final TestDataTreeListener listener = new TestDataTreeListener(latch);
171         final ListenerRegistration<TestDataTreeListener> listenerReg =
172                 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
173         writeTx = domBroker.newWriteOnlyTransaction();
174         writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER_2);
175         writeTx.submit();
176
177         latch.await(5, TimeUnit.SECONDS);
178
179         assertEquals(2, listener.getReceivedChanges().size());
180         Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
181         assertEquals(1, changes.size());
182
183         DataTreeCandidate candidate = changes.iterator().next();
184         assertNotNull(candidate);
185         DataTreeCandidateNode candidateRoot = candidate.getRootNode();
186         checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
187
188         changes = listener.getReceivedChanges().get(1);
189         assertEquals(1, changes.size());
190
191         candidate = changes.iterator().next();
192         assertNotNull(candidate);
193         candidateRoot = candidate.getRootNode();
194         checkChange(TEST_CONTAINER, TEST_CONTAINER_2, ModificationType.WRITE, candidateRoot);
195         listenerReg.close();
196     }
197
198     @Test
199     public void deleteContainerContainerInTreeTest() throws ExecutionException, InterruptedException {
200         final CountDownLatch latch = new CountDownLatch(2);
201
202         final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
203         assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
204                 dataTreeChangeService);
205
206         DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
207         writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
208         writeTx.submit().get();
209
210         final TestDataTreeListener listener = new TestDataTreeListener(latch);
211         final ListenerRegistration<TestDataTreeListener> listenerReg =
212                 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
213
214         writeTx = domBroker.newWriteOnlyTransaction();
215         writeTx.delete(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
216         writeTx.submit();
217
218         latch.await(5, TimeUnit.SECONDS);
219
220         assertEquals(2, listener.getReceivedChanges().size());
221         Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
222         assertEquals(1, changes.size());
223
224         DataTreeCandidate candidate = changes.iterator().next();
225         assertNotNull(candidate);
226         DataTreeCandidateNode candidateRoot = candidate.getRootNode();
227         checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
228
229         changes = listener.getReceivedChanges().get(1);
230         assertEquals(1, changes.size());
231
232         candidate = changes.iterator().next();
233         assertNotNull(candidate);
234         candidateRoot = candidate.getRootNode();
235         checkChange(TEST_CONTAINER, null, ModificationType.DELETE, candidateRoot);
236         listenerReg.close();
237     }
238
239     @Test
240     public void replaceChildListContainerInTreeTest() throws ExecutionException, InterruptedException {
241         final CountDownLatch latch = new CountDownLatch(2);
242
243         final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
244         assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
245                 dataTreeChangeService);
246
247         DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
248         writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
249         writeTx.submit().get();
250
251         final TestDataTreeListener listener = new TestDataTreeListener(latch);
252         final ListenerRegistration<TestDataTreeListener> listenerReg =
253                 dataTreeChangeService.registerDataTreeChangeListener(ROOT_DATA_TREE_ID, listener);
254
255         writeTx = domBroker.newWriteOnlyTransaction();
256         writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH, OUTER_LIST_2);
257         writeTx.submit();
258
259         latch.await(5, TimeUnit.SECONDS);
260
261         assertEquals(2, listener.getReceivedChanges().size());
262         Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
263         assertEquals(1, changes.size());
264
265         DataTreeCandidate candidate = changes.iterator().next();
266         assertNotNull(candidate);
267         DataTreeCandidateNode candidateRoot = candidate.getRootNode();
268         checkChange(null, TEST_CONTAINER, ModificationType.WRITE, candidateRoot);
269
270         changes = listener.getReceivedChanges().get(1);
271         assertEquals(1, changes.size());
272
273         candidate = changes.iterator().next();
274         assertNotNull(candidate);
275         candidateRoot = candidate.getRootNode();
276         checkChange(TEST_CONTAINER, TEST_CONTAINER_2, ModificationType.SUBTREE_MODIFIED, candidateRoot);
277         final DataTreeCandidateNode modifiedChild = candidateRoot.getModifiedChild(
278                 new YangInstanceIdentifier.NodeIdentifier(TestModel.OUTER_LIST_QNAME));
279         assertNotNull(modifiedChild);
280         checkChange(OUTER_LIST, OUTER_LIST_2, ModificationType.WRITE, modifiedChild);
281         listenerReg.close();
282     }
283
284     @Test
285     public void rootModificationChildListenerTest() throws ExecutionException, InterruptedException {
286         final CountDownLatch latch = new CountDownLatch(2);
287
288         final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
289         assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
290                 dataTreeChangeService);
291
292         DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
293         writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
294         writeTx.submit().get();
295
296         final TestDataTreeListener listener = new TestDataTreeListener(latch);
297         final ListenerRegistration<TestDataTreeListener> listenerReg =
298                 dataTreeChangeService.registerDataTreeChangeListener(OUTER_LIST_DATA_TREE_ID, listener);
299
300         writeTx = domBroker.newWriteOnlyTransaction();
301         writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER_2);
302         writeTx.submit().get();
303
304         latch.await(1, TimeUnit.SECONDS);
305
306         assertEquals(2, listener.getReceivedChanges().size());
307         Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
308         assertEquals(1, changes.size());
309
310         DataTreeCandidate candidate = changes.iterator().next();
311         assertNotNull(candidate);
312         DataTreeCandidateNode candidateRoot = candidate.getRootNode();
313         checkChange(null, OUTER_LIST, ModificationType.WRITE, candidateRoot);
314
315         changes = listener.getReceivedChanges().get(1);
316         assertEquals(1, changes.size());
317
318         candidate = changes.iterator().next();
319         assertNotNull(candidate);
320         candidateRoot = candidate.getRootNode();
321         checkChange(OUTER_LIST, OUTER_LIST_2, ModificationType.WRITE, candidateRoot);
322         listenerReg.close();
323     }
324
325     @Test
326     public void listEntryChangeNonRootRegistrationTest() throws ExecutionException, InterruptedException {
327         final CountDownLatch latch = new CountDownLatch(2);
328
329         final DOMDataTreeChangeService dataTreeChangeService = getDOMDataTreeChangeService();
330         assertNotNull("DOMDataTreeChangeService not found, cannot continue with test!",
331                 dataTreeChangeService);
332
333         DOMDataTreeWriteTransaction writeTx = domBroker.newWriteOnlyTransaction();
334         writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, TEST_CONTAINER);
335         writeTx.submit().get();
336
337         final TestDataTreeListener listener = new TestDataTreeListener(latch);
338         final ListenerRegistration<TestDataTreeListener> listenerReg =
339                 dataTreeChangeService.registerDataTreeChangeListener(OUTER_LIST_DATA_TREE_ID, listener);
340
341         final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListEntryId1 =
342                 new YangInstanceIdentifier.NodeIdentifierWithPredicates(
343                         TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1);
344         final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListEntryId2 =
345                 new YangInstanceIdentifier.NodeIdentifierWithPredicates(
346                         TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2);
347         final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListEntryId3 =
348                 new YangInstanceIdentifier.NodeIdentifierWithPredicates(
349                         TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 3);
350
351         final MapEntryNode outerListEntry1 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1);
352         final MapEntryNode outerListEntry2 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2);
353         final MapEntryNode outerListEntry3 = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 3);
354
355         final MapNode listAfter = ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
356                 .withChild(outerListEntry2)
357                 .withChild(outerListEntry3)
358                 .build();
359
360         writeTx = domBroker.newWriteOnlyTransaction();
361         writeTx.delete(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId1));
362         writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId2),
363                 outerListEntry2);
364         writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.OUTER_LIST_PATH.node(outerListEntryId3),
365                 outerListEntry3);
366         writeTx.submit();
367
368         latch.await(5, TimeUnit.SECONDS);
369
370         assertEquals(2, listener.getReceivedChanges().size());
371         Collection<DataTreeCandidate> changes = listener.getReceivedChanges().get(0);
372         assertEquals(1, changes.size());
373
374         DataTreeCandidate candidate = changes.iterator().next();
375         assertNotNull(candidate);
376         DataTreeCandidateNode candidateRoot = candidate.getRootNode();
377         checkChange(null, OUTER_LIST, ModificationType.WRITE, candidateRoot);
378
379         changes = listener.getReceivedChanges().get(1);
380         assertEquals(1, changes.size());
381
382         candidate = changes.iterator().next();
383         assertNotNull(candidate);
384         candidateRoot = candidate.getRootNode();
385         checkChange(OUTER_LIST, listAfter, ModificationType.SUBTREE_MODIFIED, candidateRoot);
386         final DataTreeCandidateNode entry1Canditate = candidateRoot.getModifiedChild(outerListEntryId1);
387         checkChange(outerListEntry1, null, ModificationType.DELETE, entry1Canditate);
388         final DataTreeCandidateNode entry2Canditate = candidateRoot.getModifiedChild(outerListEntryId2);
389         checkChange(null, outerListEntry2, ModificationType.WRITE, entry2Canditate);
390         final DataTreeCandidateNode entry3Canditate = candidateRoot.getModifiedChild(outerListEntryId3);
391         checkChange(null, outerListEntry3, ModificationType.WRITE, entry3Canditate);
392         listenerReg.close();
393     }
394
395     private static void checkChange(final NormalizedNode<?, ?> expectedBefore,
396                                     final NormalizedNode<?, ?> expectedAfter,
397                                     final ModificationType expectedMod,
398                                     final DataTreeCandidateNode candidateNode) {
399         if (expectedBefore != null) {
400             assertTrue(candidateNode.getDataBefore().isPresent());
401             assertEquals(expectedBefore, candidateNode.getDataBefore().get());
402         } else {
403             assertFalse(candidateNode.getDataBefore().isPresent());
404         }
405
406         if (expectedAfter != null) {
407             assertTrue(candidateNode.getDataAfter().isPresent());
408             assertEquals(expectedAfter, candidateNode.getDataAfter().get());
409         } else {
410             assertFalse(candidateNode.getDataAfter().isPresent());
411         }
412
413         assertEquals(expectedMod, candidateNode.getModificationType());
414     }
415
416     private DOMDataTreeChangeService getDOMDataTreeChangeService() {
417         final DOMDataBrokerExtension extension = domBroker.getSupportedExtensions()
418                 .get(DOMDataTreeChangeService.class);
419         if (extension == null) {
420             return null;
421         }
422         DOMDataTreeChangeService dataTreeChangeService = null;
423         if (extension instanceof DOMDataTreeChangeService) {
424             dataTreeChangeService = (DOMDataTreeChangeService) extension;
425         }
426         return dataTreeChangeService;
427     }
428
429     static class CommitExecutorService extends ForwardingExecutorService {
430
431         ExecutorService delegate;
432
433         CommitExecutorService(final ExecutorService delegate) {
434             this.delegate = delegate;
435         }
436
437         @Override
438         protected ExecutorService delegate() {
439             return delegate;
440         }
441     }
442
443     static class TestDataTreeListener implements DOMDataTreeChangeListener {
444
445         private final List<Collection<DataTreeCandidate>> receivedChanges = new ArrayList<>();
446         private final CountDownLatch latch;
447
448         TestDataTreeListener(final CountDownLatch latch) {
449             this.latch = latch;
450         }
451
452         @Override
453         public void onDataTreeChanged(@Nonnull final Collection<DataTreeCandidate> changes) {
454             receivedChanges.add(changes);
455             latch.countDown();
456         }
457
458         public List<Collection<DataTreeCandidate>> getReceivedChanges() {
459             return receivedChanges;
460         }
461     }
462 }