2 * Copyright (c) 2014 Cisco Systems, Inc. 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.mdsal.dom.store.inmemory;
10 import static org.hamcrest.CoreMatchers.containsString;
11 import static org.hamcrest.CoreMatchers.instanceOf;
12 import static org.hamcrest.MatcherAssert.assertThat;
13 import static org.junit.Assert.assertEquals;
14 import static org.junit.Assert.assertFalse;
15 import static org.junit.Assert.assertNotNull;
16 import static org.junit.Assert.assertThrows;
17 import static org.junit.Assert.assertTrue;
18 import static org.mockito.ArgumentMatchers.any;
19 import static org.mockito.Mockito.doReturn;
20 import static org.mockito.Mockito.doThrow;
21 import static org.mockito.Mockito.mock;
23 import com.google.common.util.concurrent.Futures;
24 import com.google.common.util.concurrent.ListenableFuture;
25 import com.google.common.util.concurrent.MoreExecutors;
26 import java.util.Optional;
27 import java.util.concurrent.ExecutionException;
28 import org.junit.AfterClass;
29 import org.junit.Before;
30 import org.junit.BeforeClass;
31 import org.junit.Ignore;
32 import org.junit.Test;
33 import org.opendaylight.mdsal.common.api.ReadFailedException;
34 import org.opendaylight.mdsal.dom.spi.store.DOMStoreReadTransaction;
35 import org.opendaylight.mdsal.dom.spi.store.DOMStoreReadWriteTransaction;
36 import org.opendaylight.mdsal.dom.spi.store.DOMStoreThreePhaseCommitCohort;
37 import org.opendaylight.mdsal.dom.spi.store.DOMStoreTransactionChain;
38 import org.opendaylight.mdsal.dom.spi.store.DOMStoreWriteTransaction;
39 import org.opendaylight.mdsal.dom.spi.store.SnapshotBackedTransactions;
40 import org.opendaylight.mdsal.dom.spi.store.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
41 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
42 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
43 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
44 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
45 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
46 import org.opendaylight.yangtools.yang.data.spi.node.ImmutableNodes;
47 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeModification;
48 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeSnapshot;
49 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
51 public class InMemoryDataStoreTest {
52 private static EffectiveModelContext SCHEMA_CONTEXT;
54 private InMemoryDOMDataStore domStore;
57 public static void beforeClass() {
58 SCHEMA_CONTEXT = TestModel.createTestContext();
62 public static void afterClass() {
63 SCHEMA_CONTEXT = null;
67 public void setupStore() {
68 domStore = new InMemoryDOMDataStore("TEST", MoreExecutors.newDirectExecutorService());
69 domStore.onModelContextUpdated(SCHEMA_CONTEXT);
73 public void testTransactionIsolation() throws Exception {
74 DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
75 assertNotNull(readTx);
77 DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
78 assertNotNull(writeTx);
81 * Writes /test in writeTx.
83 final var testNode = testContainer();
84 writeTx.write(TestModel.TEST_PATH, testNode);
87 * Reads /test from writeTx Read should return container.
89 assertEquals(Optional.of(testNode), Futures.getDone(writeTx.read(TestModel.TEST_PATH)));
92 * Reads /test from readTx Read should return Absent.
94 assertEquals(Optional.empty(), Futures.getDone(readTx.read(TestModel.TEST_PATH)));
97 private static ContainerNode testContainer() {
98 return ImmutableNodes.newContainerBuilder()
99 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
104 public void testTransactionCommit() throws Exception {
106 DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
107 assertNotNull(writeTx);
110 * Writes /test in writeTx.
112 var testNode = testContainer();
113 writeTx.write(TestModel.TEST_PATH, testNode);
116 * Reads /test from writeTx Read should return container.
118 assertEquals(Optional.of(testNode), Futures.getDone(writeTx.read(TestModel.TEST_PATH)));
120 DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
122 assertThreePhaseCommit(cohort);
124 assertEquals(Optional.of(testNode),
125 Futures.getDone(domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)));
129 public void testDelete() throws Exception {
131 DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
132 assertNotNull(writeTx);
134 // Write /test and commit
136 writeTx.write(TestModel.TEST_PATH, testContainer());
138 assertThreePhaseCommit(writeTx.ready());
140 assertTrue(Futures.getDone(domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)).isPresent());
142 // Delete /test and verify
144 writeTx = domStore.newWriteOnlyTransaction();
146 writeTx.delete(TestModel.TEST_PATH);
148 assertThreePhaseCommit(writeTx.ready());
150 assertEquals(Optional.empty(), Futures.getDone(domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)));
154 public void testMerge() throws Exception {
156 DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
157 assertNotNull(writeTx);
159 ContainerNode containerNode = ImmutableNodes.newContainerBuilder()
160 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
161 .addChild(ImmutableNodes.newSystemMapBuilder()
162 .withNodeIdentifier(new NodeIdentifier(TestModel.OUTER_LIST_QNAME))
163 .addChild(ImmutableNodes.newMapEntryBuilder()
164 .withNodeIdentifier(NodeIdentifierWithPredicates.of(TestModel.OUTER_LIST_QNAME,
165 TestModel.ID_QNAME, 1))
166 .withChild(ImmutableNodes.leafNode(TestModel.ID_QNAME, 1))
171 writeTx.merge(TestModel.TEST_PATH, containerNode);
173 assertThreePhaseCommit(writeTx.ready());
175 assertEquals(Optional.of(containerNode),
176 Futures.getDone(domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)));
178 // Merge a new list entry node
180 writeTx = domStore.newWriteOnlyTransaction();
181 assertNotNull(writeTx);
183 containerNode = ImmutableNodes.newContainerBuilder()
184 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
185 .addChild(ImmutableNodes.newSystemMapBuilder()
186 .withNodeIdentifier(new NodeIdentifier(TestModel.OUTER_LIST_QNAME))
187 .addChild(ImmutableNodes.newMapEntryBuilder()
188 .withNodeIdentifier(NodeIdentifierWithPredicates.of(TestModel.OUTER_LIST_QNAME,
189 TestModel.ID_QNAME, 1))
190 .withChild(ImmutableNodes.leafNode(TestModel.ID_QNAME, 1))
192 .addChild(ImmutableNodes.newMapEntryBuilder()
193 .withNodeIdentifier(NodeIdentifierWithPredicates.of(TestModel.OUTER_LIST_QNAME,
194 TestModel.ID_QNAME, 2))
195 .withChild(ImmutableNodes.leafNode(TestModel.ID_QNAME, 2))
200 writeTx.merge(TestModel.TEST_PATH, containerNode);
202 assertThreePhaseCommit(writeTx.ready());
204 assertEquals(Optional.of(containerNode),
205 Futures.getDone(domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)));
209 public void testExistsForExistingData() throws Exception {
211 DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
212 assertNotNull(writeTx);
214 ContainerNode containerNode = ImmutableNodes.newContainerBuilder()
215 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
216 .addChild(ImmutableNodes.newSystemMapBuilder()
217 .withNodeIdentifier(new NodeIdentifier(TestModel.OUTER_LIST_QNAME))
218 .addChild(ImmutableNodes.newMapEntryBuilder()
219 .withNodeIdentifier(NodeIdentifierWithPredicates.of(TestModel.OUTER_LIST_QNAME,
220 TestModel.ID_QNAME, 1))
221 .withChild(ImmutableNodes.leafNode(TestModel.ID_QNAME, 1))
226 writeTx.merge(TestModel.TEST_PATH, containerNode);
228 assertEquals(Boolean.TRUE, Futures.getDone(writeTx.exists(TestModel.TEST_PATH)));
230 DOMStoreThreePhaseCommitCohort ready = writeTx.ready();
232 ready.preCommit().get();
234 ready.commit().get();
236 DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
237 assertNotNull(readTx);
239 assertEquals(Boolean.TRUE, Futures.getDone(readTx.exists(TestModel.TEST_PATH)));
243 public void testExistsForNonExistingData() throws Exception {
245 DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
246 assertNotNull(writeTx);
248 var exists = writeTx.exists(TestModel.TEST_PATH);
250 assertEquals(Boolean.FALSE, exists.get());
252 var readTx = domStore.newReadOnlyTransaction();
253 assertNotNull(readTx);
255 exists = readTx.exists(TestModel.TEST_PATH);
257 assertEquals(Boolean.FALSE, exists.get());
261 public void testExistsThrowsReadFailedException() {
262 var readTx = domStore.newReadOnlyTransaction();
263 assertNotNull(readTx);
267 final var future = readTx.exists(TestModel.TEST_PATH);
269 final var ex = assertThrows(ExecutionException.class, future::get).getCause();
270 assertThat(ex, instanceOf(ReadFailedException.class));
275 public void testReadWithReadOnlyTransactionClosed() {
276 DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
277 assertNotNull(readTx);
281 assertReadThrows(readTx);
285 public void testReadWithReadOnlyTransactionFailure() {
286 DataTreeSnapshot mockSnapshot = mock(DataTreeSnapshot.class);
287 doThrow(new RuntimeException("mock ex")).when(mockSnapshot)
288 .readNode(any(YangInstanceIdentifier.class));
290 DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadTransaction("1", true, mockSnapshot);
292 assertReadThrows(readTx);
296 public void testReadWithReadWriteTransactionClosed() {
298 DOMStoreReadTransaction readTx = domStore.newReadWriteTransaction();
299 assertNotNull(readTx);
303 assertReadThrows(readTx);
307 public void testReadWithReadWriteTransactionFailure() {
308 DataTreeSnapshot mockSnapshot = mock(DataTreeSnapshot.class);
309 DataTreeModification mockModification = mock(DataTreeModification.class);
310 doThrow(new RuntimeException("mock ex")).when(mockModification)
311 .readNode(any(YangInstanceIdentifier.class));
312 doReturn(mockModification).when(mockSnapshot).newModification();
313 @SuppressWarnings("unchecked")
314 TransactionReadyPrototype<String> mockReady = mock(TransactionReadyPrototype.class);
315 DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadWriteTransaction(
316 "1", false, mockSnapshot, mockReady);
318 assertReadThrows(readTx);
321 private static void assertReadThrows(final DOMStoreReadTransaction readTx) {
322 final var future = readTx.read(TestModel.TEST_PATH);
323 final var cause = assertThrows(ExecutionException.class, future::get).getCause();
324 assertThat(cause, instanceOf(ReadFailedException.class));
328 public void testWriteWithTransactionReady() {
329 var writeTx = domStore.newWriteOnlyTransaction();
333 assertThrows(IllegalStateException.class, () -> writeTx.write(TestModel.TEST_PATH, testContainer()));
337 public void testReadyWithTransactionAlreadyReady() {
338 var writeTx = domStore.newWriteOnlyTransaction();
343 assertThrows(IllegalStateException.class, writeTx::ready);
347 public void testReadyWithMissingMandatoryData() throws Exception {
348 DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
349 var testNode = ImmutableNodes.newContainerBuilder()
350 .withNodeIdentifier(new NodeIdentifier(TestModel.MANDATORY_DATA_TEST_QNAME))
351 .addChild(ImmutableNodes.leafNode(TestModel.OPTIONAL_QNAME, "data"))
353 writeTx.write(TestModel.MANDATORY_DATA_TEST_PATH, testNode);
354 DOMStoreThreePhaseCommitCohort ready = writeTx.ready();
356 final var future = ready.canCommit();
357 final var cause = assertThrows(ExecutionException.class, future::get).getCause();
358 assertThat(cause, instanceOf(IllegalArgumentException.class));
359 assertThat(cause.getMessage(), containsString("mandatory-data-test is missing mandatory descendant"));
363 public void testTransactionAbort() throws Exception {
365 DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
366 assertNotNull(writeTx);
368 assertTestContainerWrite(writeTx);
370 DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
372 assertTrue(cohort.canCommit().get());
373 cohort.preCommit().get();
374 cohort.abort().get();
376 assertEquals(Optional.empty(), domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get());
380 public void testTransactionChain() throws Exception {
381 DOMStoreTransactionChain txChain = domStore.createTransactionChain();
382 assertNotNull(txChain);
385 * We alocate new read-write transaction and write /test.
387 DOMStoreReadWriteTransaction firstTx = txChain.newReadWriteTransaction();
388 assertTestContainerWrite(firstTx);
391 * First transaction is marked as ready, we are able to allocate chained
394 final DOMStoreThreePhaseCommitCohort firstWriteTxCohort = firstTx.ready();
397 * We alocate chained transaction - read transaction, note first one is
398 * still not commited to datastore.
400 DOMStoreReadTransaction secondReadTx = txChain.newReadOnlyTransaction();
403 * We test if we are able to read data from tx, read should not fail
404 * since we are using chained transaction.
406 assertTestContainerExists(secondReadTx);
409 * We alocate next transaction, which is still based on first one, but
412 DOMStoreReadWriteTransaction thirdDeleteTx = txChain.newReadWriteTransaction();
415 * We test existence of /test in third transaction container should
416 * still be visible from first one (which is still uncommmited).
418 assertTestContainerExists(thirdDeleteTx);
421 * We delete node in third transaction.
423 thirdDeleteTx.delete(TestModel.TEST_PATH);
426 * third transaction is sealed.
428 DOMStoreThreePhaseCommitCohort thirdDeleteTxCohort = thirdDeleteTx.ready();
431 * We commit first transaction.
434 assertThreePhaseCommit(firstWriteTxCohort);
436 // Alocates store transacion
437 DOMStoreReadTransaction storeReadTx = domStore.newReadOnlyTransaction();
439 * We verify transaction is commited to store, container should exists
442 assertTestContainerExists(storeReadTx);
444 * We commit third transaction
447 assertThreePhaseCommit(thirdDeleteTxCohort);
452 public void testTransactionConflict() throws Exception {
453 DOMStoreReadWriteTransaction txOne = domStore.newReadWriteTransaction();
454 DOMStoreReadWriteTransaction txTwo = domStore.newReadWriteTransaction();
455 assertTestContainerWrite(txOne);
456 assertTestContainerWrite(txTwo);
459 * Commits transaction
461 assertThreePhaseCommit(txOne.ready());
464 * Asserts that txTwo could not be commited
466 assertFalse(txTwo.ready().canCommit().get());
469 private static void assertThreePhaseCommit(final DOMStoreThreePhaseCommitCohort cohort) throws Exception {
470 assertTrue(cohort.canCommit().get());
471 cohort.preCommit().get();
472 cohort.commit().get();
475 private static Optional<NormalizedNode> assertTestContainerWrite(final DOMStoreReadWriteTransaction writeTx)
479 * Writes /test in writeTx
482 writeTx.write(TestModel.TEST_PATH, testContainer());
484 return assertTestContainerExists(writeTx);
488 * Reads /test from readTx Read should return container.
490 private static Optional<NormalizedNode> assertTestContainerExists(final DOMStoreReadTransaction readTx)
493 ListenableFuture<Optional<NormalizedNode>> writeTxContainer = readTx.read(TestModel.TEST_PATH);
494 assertTrue(writeTxContainer.get().isPresent());
495 return writeTxContainer.get();