2 * Copyright (c) 2017 Inocybe Technologies and others. All rights reserved.
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
8 package org.opendaylight.controller.cluster.databroker.compat;
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertNotNull;
12 import static org.junit.Assert.assertTrue;
13 import static org.junit.Assert.fail;
14 import static org.mockito.Matchers.any;
15 import static org.mockito.Matchers.anyObject;
16 import static org.mockito.Matchers.eq;
17 import static org.mockito.Mockito.doAnswer;
18 import static org.mockito.Mockito.doNothing;
19 import static org.mockito.Mockito.doReturn;
20 import static org.mockito.Mockito.inOrder;
21 import static org.mockito.Mockito.mock;
22 import static org.mockito.Mockito.verify;
24 import com.google.common.base.Optional;
25 import com.google.common.collect.ImmutableMap;
26 import com.google.common.util.concurrent.CheckedFuture;
27 import com.google.common.util.concurrent.Futures;
28 import com.google.common.util.concurrent.MoreExecutors;
29 import java.util.Arrays;
30 import java.util.Collection;
31 import java.util.concurrent.ExecutionException;
32 import java.util.concurrent.TimeUnit;
33 import java.util.concurrent.TimeoutException;
34 import org.junit.Before;
35 import org.junit.Test;
36 import org.mockito.ArgumentCaptor;
37 import org.mockito.InOrder;
38 import org.mockito.Mock;
39 import org.mockito.MockitoAnnotations;
40 import org.opendaylight.controller.cluster.databroker.ConcurrentDOMDataBroker;
41 import org.opendaylight.controller.cluster.datastore.DistributedDataStoreInterface;
42 import org.opendaylight.controller.cluster.datastore.exceptions.NoShardLeaderException;
43 import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
44 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
45 import org.opendaylight.controller.md.sal.common.api.data.DataStoreUnavailableException;
46 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
47 import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
48 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
49 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
50 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
51 import org.opendaylight.controller.md.sal.dom.api.ClusteredDOMDataTreeChangeListener;
52 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
53 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
54 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener;
55 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeService;
56 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeCommitCohortRegistry;
57 import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
58 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
59 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
60 import org.opendaylight.mdsal.dom.api.DOMDataTreeCommitCohort;
61 import org.opendaylight.mdsal.dom.api.DOMDataTreeCommitCohortRegistration;
62 import org.opendaylight.mdsal.dom.spi.store.DOMStoreReadTransaction;
63 import org.opendaylight.mdsal.dom.spi.store.DOMStoreReadWriteTransaction;
64 import org.opendaylight.mdsal.dom.spi.store.DOMStoreThreePhaseCommitCohort;
65 import org.opendaylight.mdsal.dom.spi.store.DOMStoreTransactionChain;
66 import org.opendaylight.mdsal.dom.spi.store.DOMStoreTreeChangePublisher;
67 import org.opendaylight.mdsal.dom.spi.store.DOMStoreWriteTransaction;
68 import org.opendaylight.yangtools.concepts.ListenerRegistration;
69 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
70 import org.opendaylight.yangtools.yang.data.api.schema.tree.ConflictingModificationAppliedException;
71 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
72 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
73 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
76 * Unit tests for LegacyDOMDataBrokerAdapter.
78 * @author Thomas Pantelis
80 public class LegacyDOMDataBrokerAdapterTest {
82 private TestDOMStore mockOperStore;
85 private TestDOMStore mockConfigStore;
88 private DOMStoreReadTransaction mockReadTx;
91 private DOMStoreWriteTransaction mockWriteTx;
94 private DOMStoreReadWriteTransaction mockReadWriteTx;
97 private DOMStoreThreePhaseCommitCohort mockCommitCohort;
99 private LegacyDOMDataBrokerAdapter adapter;
100 private NormalizedNode<?,?> dataNode;
103 public void setup() {
104 MockitoAnnotations.initMocks(this);
106 ConcurrentDOMDataBroker backendBroker = new ConcurrentDOMDataBroker(ImmutableMap.of(
107 org.opendaylight.mdsal.common.api.LogicalDatastoreType.OPERATIONAL, mockOperStore,
108 org.opendaylight.mdsal.common.api.LogicalDatastoreType.CONFIGURATION, mockConfigStore),
109 MoreExecutors.newDirectExecutorService());
111 adapter = new LegacyDOMDataBrokerAdapter(backendBroker);
113 doReturn(Futures.immediateFuture(Boolean.TRUE)).when(mockCommitCohort).canCommit();
114 doReturn(Futures.immediateFuture(null)).when(mockCommitCohort).preCommit();
115 doReturn(Futures.immediateFuture(null)).when(mockCommitCohort).commit();
116 doReturn(Futures.immediateFuture(null)).when(mockCommitCohort).abort();
118 dataNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
120 doReturn(mockWriteTx).when(mockConfigStore).newWriteOnlyTransaction();
121 doNothing().when(mockWriteTx).write(TestModel.TEST_PATH, dataNode);
122 doNothing().when(mockWriteTx).merge(TestModel.TEST_PATH, dataNode);
123 doNothing().when(mockWriteTx).delete(TestModel.TEST_PATH);
124 doNothing().when(mockWriteTx).close();
125 doReturn(mockCommitCohort).when(mockWriteTx).ready();
127 doReturn(mockReadTx).when(mockConfigStore).newReadOnlyTransaction();
128 doReturn(Futures.immediateCheckedFuture(Optional.of(dataNode))).when(mockReadTx).read(TestModel.TEST_PATH);
129 doReturn(Futures.immediateCheckedFuture(Boolean.TRUE)).when(mockReadTx).exists(TestModel.TEST_PATH);
131 doReturn(mockReadWriteTx).when(mockConfigStore).newReadWriteTransaction();
132 doNothing().when(mockReadWriteTx).write(TestModel.TEST_PATH, dataNode);
133 doReturn(mockCommitCohort).when(mockReadWriteTx).ready();
134 doReturn(Futures.immediateCheckedFuture(Optional.of(dataNode))).when(mockReadWriteTx).read(TestModel.TEST_PATH);
136 DOMStoreTransactionChain mockTxChain = mock(DOMStoreTransactionChain.class);
137 doReturn(mockReadTx).when(mockTxChain).newReadOnlyTransaction();
138 doReturn(mockWriteTx).when(mockTxChain).newWriteOnlyTransaction();
139 doReturn(mockReadWriteTx).when(mockTxChain).newReadWriteTransaction();
140 doReturn(mockTxChain).when(mockConfigStore).createTransactionChain();
142 doReturn(mock(DOMStoreTransactionChain.class)).when(mockOperStore).createTransactionChain();
146 public void testReadOnlyTransaction() throws Exception {
147 DOMDataReadOnlyTransaction tx = adapter.newReadOnlyTransaction();
149 // Test successful read
151 CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> readFuture =
152 tx.read(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
153 Optional<NormalizedNode<?, ?>> readOptional = readFuture.get();
154 assertEquals("isPresent", true, readOptional.isPresent());
155 assertEquals("NormalizedNode", dataNode, readOptional.get());
157 // Test successful exists
159 CheckedFuture<Boolean, ReadFailedException> existsFuture =
160 tx.exists(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
161 assertEquals("exists", Boolean.TRUE, existsFuture.get());
165 String errorMsg = "mock read error";
166 Throwable cause = new RuntimeException();
167 doReturn(Futures.immediateFailedCheckedFuture(new org.opendaylight.mdsal.common.api.ReadFailedException(
168 errorMsg, cause))).when(mockReadTx).read(TestModel.TEST_PATH);
171 tx.read(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH).checkedGet();
172 fail("Expected ReadFailedException");
173 } catch (ReadFailedException e) {
174 assertEquals("getMessage", errorMsg, e.getMessage());
175 assertEquals("getCause", cause, e.getCause());
180 verify(mockReadTx).close();
184 public void testWriteOnlyTransaction() throws Exception {
185 // Test successful write operations and submit
187 DOMDataWriteTransaction tx = adapter.newWriteOnlyTransaction();
189 tx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, dataNode);
190 verify(mockWriteTx).write(TestModel.TEST_PATH, dataNode);
192 tx.merge(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, dataNode);
193 verify(mockWriteTx).merge(TestModel.TEST_PATH, dataNode);
195 tx.delete(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
196 verify(mockWriteTx).delete(TestModel.TEST_PATH);
198 tx.commit().get(5, TimeUnit.SECONDS);
200 InOrder inOrder = inOrder(mockCommitCohort);
201 inOrder.verify(mockCommitCohort).canCommit();
202 inOrder.verify(mockCommitCohort).preCommit();
203 inOrder.verify(mockCommitCohort).commit();
207 tx = adapter.newWriteOnlyTransaction();
208 tx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, dataNode);
210 verify(mockWriteTx).close();
212 // Test submit with OptimisticLockFailedException
214 String errorMsg = "mock OptimisticLockFailedException";
215 Throwable cause = new ConflictingModificationAppliedException(TestModel.TEST_PATH, "mock");
216 doReturn(Futures.immediateFailedFuture(new org.opendaylight.mdsal.common.api.OptimisticLockFailedException(
217 errorMsg, cause))).when(mockCommitCohort).canCommit();
220 tx = adapter.newWriteOnlyTransaction();
221 tx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, dataNode);
223 fail("Expected OptimisticLockFailedException");
224 } catch (OptimisticLockFailedException e) {
225 assertEquals("getMessage", errorMsg, e.getMessage());
226 assertEquals("getCause", cause, e.getCause());
229 // Test submit with TransactionCommitFailedException
231 errorMsg = "mock TransactionCommitFailedException";
232 cause = new DataValidationFailedException(TestModel.TEST_PATH, "mock");
233 doReturn(Futures.immediateFailedFuture(new org.opendaylight.mdsal.common.api.TransactionCommitFailedException(
234 errorMsg, cause))).when(mockCommitCohort).canCommit();
237 tx = adapter.newWriteOnlyTransaction();
238 tx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, dataNode);
240 fail("Expected TransactionCommitFailedException");
241 } catch (TransactionCommitFailedException e) {
242 assertEquals("getMessage", errorMsg, e.getMessage());
243 assertEquals("getCause", cause, e.getCause());
246 // Test submit with DataStoreUnavailableException
248 errorMsg = "mock NoShardLeaderException";
249 cause = new NoShardLeaderException("mock");
250 doReturn(Futures.immediateFailedFuture(cause)).when(mockCommitCohort).canCommit();
253 tx = adapter.newWriteOnlyTransaction();
254 tx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, dataNode);
256 fail("Expected TransactionCommitFailedException");
257 } catch (TransactionCommitFailedException e) {
258 assertEquals("getCause type", DataStoreUnavailableException.class, e.getCause().getClass());
259 assertEquals("Root cause", cause, e.getCause().getCause());
262 // Test submit with RuntimeException
264 errorMsg = "mock RuntimeException";
265 cause = new RuntimeException(errorMsg);
266 doReturn(Futures.immediateFailedFuture(cause)).when(mockCommitCohort).canCommit();
269 tx = adapter.newWriteOnlyTransaction();
270 tx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, dataNode);
272 fail("Expected TransactionCommitFailedException");
273 } catch (TransactionCommitFailedException e) {
274 assertEquals("getCause", cause, e.getCause());
279 public void testReadWriteTransaction() throws Exception {
280 DOMDataReadWriteTransaction tx = adapter.newReadWriteTransaction();
282 CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> readFuture =
283 tx.read(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
284 Optional<NormalizedNode<?, ?>> readOptional = readFuture.get();
285 assertEquals("isPresent", true, readOptional.isPresent());
286 assertEquals("NormalizedNode", dataNode, readOptional.get());
288 tx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, dataNode);
289 verify(mockReadWriteTx).write(TestModel.TEST_PATH, dataNode);
291 tx.commit().get(5, TimeUnit.SECONDS);
293 InOrder inOrder = inOrder(mockCommitCohort);
294 inOrder.verify(mockCommitCohort).canCommit();
295 inOrder.verify(mockCommitCohort).preCommit();
296 inOrder.verify(mockCommitCohort).commit();
299 @SuppressWarnings("rawtypes")
301 public void testTransactionChain() throws Exception {
302 TransactionChainListener mockListener = mock(TransactionChainListener.class);
303 doNothing().when(mockListener).onTransactionChainSuccessful(anyObject());
304 doNothing().when(mockListener).onTransactionChainFailed(anyObject(), anyObject(), anyObject());
306 DOMTransactionChain chain = adapter.createTransactionChain(mockListener);
310 DOMDataReadOnlyTransaction readTx = chain.newReadOnlyTransaction();
312 CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> readFuture =
313 readTx.read(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
314 Optional<NormalizedNode<?, ?>> readOptional = readFuture.get();
315 assertEquals("isPresent", true, readOptional.isPresent());
316 assertEquals("NormalizedNode", dataNode, readOptional.get());
318 // Test write-only tx
320 DOMDataWriteTransaction writeTx = chain.newWriteOnlyTransaction();
322 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, dataNode);
323 verify(mockWriteTx).write(TestModel.TEST_PATH, dataNode);
324 writeTx.commit().get(5, TimeUnit.SECONDS);
326 InOrder inOrder = inOrder(mockCommitCohort);
327 inOrder.verify(mockCommitCohort).canCommit();
328 inOrder.verify(mockCommitCohort).preCommit();
329 inOrder.verify(mockCommitCohort).commit();
331 // Test read-write tx
333 DOMDataReadWriteTransaction readWriteTx = chain.newReadWriteTransaction();
335 readFuture = readWriteTx.read(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
336 readOptional = readFuture.get();
337 assertEquals("isPresent", true, readOptional.isPresent());
338 assertEquals("NormalizedNode", dataNode, readOptional.get());
341 verify(mockListener).onTransactionChainSuccessful(chain);
345 doReturn(Futures.immediateFailedFuture(new org.opendaylight.mdsal.common.api.TransactionCommitFailedException(
346 "mock", (Throwable)null))).when(mockCommitCohort).canCommit();
348 chain = adapter.createTransactionChain(mockListener);
350 writeTx = chain.newWriteOnlyTransaction();
353 writeTx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, dataNode);
355 fail("Expected TransactionCommitFailedException");
356 } catch (TransactionCommitFailedException e) {
360 ArgumentCaptor<AsyncTransaction> failedTx = ArgumentCaptor.forClass(AsyncTransaction.class);
361 verify(mockListener).onTransactionChainFailed(eq(chain), failedTx.capture(),
362 any(TransactionCommitFailedException.class));
365 @SuppressWarnings("unchecked")
367 public void testDataTreeChangeListener() {
368 DOMDataTreeChangeService domDTCLService =
369 (DOMDataTreeChangeService) adapter.getSupportedExtensions().get(DOMDataTreeChangeService.class);
370 assertNotNull("DOMDataTreeChangeService not found", domDTCLService);
372 ArgumentCaptor<org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener> storeDTCL =
373 ArgumentCaptor.forClass(org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener.class);
374 ListenerRegistration<org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener> mockReg =
375 mock(ListenerRegistration.class);
376 doNothing().when(mockReg).close();
377 doAnswer(invocation -> storeDTCL.getValue()).when(mockReg).getInstance();
378 doReturn(mockReg).when(mockConfigStore).registerTreeChangeListener(eq(TestModel.TEST_PATH),
379 storeDTCL.capture());
381 DOMDataTreeChangeListener brokerDTCL = mock(DOMDataTreeChangeListener.class);
382 ListenerRegistration<DOMDataTreeChangeListener> reg = domDTCLService.registerDataTreeChangeListener(
383 new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH), brokerDTCL);
384 assertEquals("getInstance", brokerDTCL, reg.getInstance());
386 Collection<DataTreeCandidate> changes = Arrays.asList(mock(DataTreeCandidate.class));
387 storeDTCL.getValue().onDataTreeChanged(changes);
388 verify(brokerDTCL).onDataTreeChanged(changes);
391 verify(mockReg).close();
393 // Test ClusteredDOMDataTreeChangeListener
395 ArgumentCaptor<org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener> storeClusteredDTCL =
396 ArgumentCaptor.forClass(org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener.class);
397 mockReg = mock(ListenerRegistration.class);
398 doReturn(mockReg).when(mockConfigStore).registerTreeChangeListener(eq(TestModel.TEST_PATH),
399 storeClusteredDTCL.capture());
401 final ClusteredDOMDataTreeChangeListener brokerClusteredDTCL = mock(ClusteredDOMDataTreeChangeListener.class);
402 domDTCLService.registerDataTreeChangeListener(new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION,
403 TestModel.TEST_PATH), brokerClusteredDTCL);
405 assertTrue("Expected ClusteredDOMDataTreeChangeListener: " + storeClusteredDTCL.getValue(),
406 storeClusteredDTCL.getValue()
407 instanceof org.opendaylight.mdsal.dom.api.ClusteredDOMDataTreeChangeListener);
408 storeClusteredDTCL.getValue().onDataTreeChanged(changes);
409 verify(brokerClusteredDTCL).onDataTreeChanged(changes);
412 @SuppressWarnings("unchecked")
414 public void testDataTreeCommitCohortRegistry() {
415 DOMDataTreeCommitCohortRegistry domCohortRegistry = (DOMDataTreeCommitCohortRegistry)
416 adapter.getSupportedExtensions().get(DOMDataTreeCommitCohortRegistry.class);
417 assertNotNull("DOMDataTreeCommitCohortRegistry not found", domCohortRegistry);
419 DOMDataTreeCommitCohort mockCohort = mock(DOMDataTreeCommitCohort.class);
420 org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier treeId =
421 new org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier(
422 org.opendaylight.mdsal.common.api.LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH);
423 DOMDataTreeCommitCohortRegistration<DOMDataTreeCommitCohort> mockReg =
424 mock(DOMDataTreeCommitCohortRegistration.class);
425 doReturn(mockReg).when(mockConfigStore).registerCommitCohort(treeId, mockCohort);
427 DOMDataTreeCommitCohortRegistration<DOMDataTreeCommitCohort> reg = domCohortRegistry.registerCommitCohort(
429 assertEquals("DOMDataTreeCommitCohortRegistration", mockReg, reg);
431 verify(mockConfigStore).registerCommitCohort(treeId, mockCohort);
436 public void testSubmit() throws Exception {
437 DOMDataWriteTransaction tx = adapter.newWriteOnlyTransaction();
439 tx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, dataNode);
440 verify(mockWriteTx).write(TestModel.TEST_PATH, dataNode);
442 tx.submit().get(5, TimeUnit.SECONDS);
444 InOrder inOrder = inOrder(mockCommitCohort);
445 inOrder.verify(mockCommitCohort).canCommit();
446 inOrder.verify(mockCommitCohort).preCommit();
447 inOrder.verify(mockCommitCohort).commit();
449 String errorMsg = "mock OptimisticLockFailedException";
450 Throwable cause = new ConflictingModificationAppliedException(TestModel.TEST_PATH, "mock");
451 doReturn(Futures.immediateFailedFuture(new org.opendaylight.mdsal.common.api.TransactionCommitFailedException(
452 errorMsg, cause))).when(mockCommitCohort).canCommit();
455 tx = adapter.newWriteOnlyTransaction();
456 tx.put(LogicalDatastoreType.CONFIGURATION, TestModel.TEST_PATH, dataNode);
458 fail("Expected TransactionCommitFailedException");
459 } catch (TransactionCommitFailedException e) {
460 assertEquals("getMessage", errorMsg, e.getMessage());
461 assertEquals("getCause", cause, e.getCause());
465 @SuppressWarnings("checkstyle:AvoidHidingCauseException")
466 private static void commit(DOMDataWriteTransaction tx)
467 throws TransactionCommitFailedException, InterruptedException, TimeoutException {
469 tx.commit().get(5, TimeUnit.SECONDS);
470 } catch (ExecutionException e) {
471 assertTrue("Expected TransactionCommitFailedException. Actual: " + e.getCause(),
472 e.getCause() instanceof TransactionCommitFailedException);
473 throw (TransactionCommitFailedException)e.getCause();
477 private interface TestDOMStore extends DistributedDataStoreInterface, DOMStoreTreeChangePublisher,
478 org.opendaylight.mdsal.dom.api.DOMDataTreeCommitCohortRegistry {