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.ListenableFuture;
24 import com.google.common.util.concurrent.MoreExecutors;
25 import java.util.Optional;
26 import java.util.concurrent.ExecutionException;
27 import org.junit.AfterClass;
28 import org.junit.Before;
29 import org.junit.BeforeClass;
30 import org.junit.Ignore;
31 import org.junit.Test;
32 import org.opendaylight.mdsal.common.api.ReadFailedException;
33 import org.opendaylight.mdsal.dom.spi.store.DOMStoreReadTransaction;
34 import org.opendaylight.mdsal.dom.spi.store.DOMStoreReadWriteTransaction;
35 import org.opendaylight.mdsal.dom.spi.store.DOMStoreThreePhaseCommitCohort;
36 import org.opendaylight.mdsal.dom.spi.store.DOMStoreTransactionChain;
37 import org.opendaylight.mdsal.dom.spi.store.DOMStoreWriteTransaction;
38 import org.opendaylight.mdsal.dom.spi.store.SnapshotBackedTransactions;
39 import org.opendaylight.mdsal.dom.spi.store.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
40 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
41 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
42 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
43 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
44 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
45 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
46 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeModification;
47 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeSnapshot;
48 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
50 public class InMemoryDataStoreTest {
51 private static EffectiveModelContext SCHEMA_CONTEXT;
53 private InMemoryDOMDataStore domStore;
56 public static void beforeClass() {
57 SCHEMA_CONTEXT = TestModel.createTestContext();
61 public static void afterClass() {
62 SCHEMA_CONTEXT = null;
66 public void setupStore() {
67 domStore = new InMemoryDOMDataStore("TEST", MoreExecutors.newDirectExecutorService());
68 domStore.onModelContextUpdated(SCHEMA_CONTEXT);
72 public void testTransactionIsolation() throws Exception {
73 DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
74 assertNotNull(readTx);
76 DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
77 assertNotNull(writeTx);
80 * Writes /test in writeTx.
82 NormalizedNode testNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
83 writeTx.write(TestModel.TEST_PATH, testNode);
86 * Reads /test from writeTx Read should return container.
88 ListenableFuture<Optional<NormalizedNode>> writeTxContainer = writeTx.read(TestModel.TEST_PATH);
89 assertEquals("read: isPresent", true, writeTxContainer.get().isPresent());
90 assertEquals("read: data", testNode, writeTxContainer.get().get());
93 * Reads /test from readTx Read should return Absent.
95 ListenableFuture<Optional<NormalizedNode>> readTxContainer = readTx.read(TestModel.TEST_PATH);
96 assertEquals("read: isPresent", false, readTxContainer.get().isPresent());
100 public void testTransactionCommit() throws Exception {
102 DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
103 assertNotNull(writeTx);
106 * Writes /test in writeTx.
108 NormalizedNode testNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
109 writeTx.write(TestModel.TEST_PATH, testNode);
112 * Reads /test from writeTx Read should return container.
114 ListenableFuture<Optional<NormalizedNode>> writeTxContainer = writeTx.read(TestModel.TEST_PATH);
115 assertEquals("read: isPresent", true, writeTxContainer.get().isPresent());
116 assertEquals("read: data", testNode, writeTxContainer.get().get());
118 DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
120 assertThreePhaseCommit(cohort);
122 Optional<NormalizedNode> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get();
123 assertEquals("After commit read: isPresent", true, afterCommitRead.isPresent());
124 assertEquals("After commit read: data", testNode, afterCommitRead.get());
128 public void testDelete() throws Exception {
130 DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
131 assertNotNull(writeTx);
133 // Write /test and commit
135 writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
137 assertThreePhaseCommit(writeTx.ready());
139 Optional<NormalizedNode> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get();
140 assertEquals("After commit read: isPresent", true, afterCommitRead.isPresent());
142 // Delete /test and verify
144 writeTx = domStore.newWriteOnlyTransaction();
146 writeTx.delete(TestModel.TEST_PATH);
148 assertThreePhaseCommit(writeTx.ready());
150 afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get();
151 assertEquals("After commit read: isPresent", false, afterCommitRead.isPresent());
155 public void testMerge() throws Exception {
157 DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
158 assertNotNull(writeTx);
160 ContainerNode containerNode = Builders.containerBuilder()
161 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
162 .addChild(ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
163 .addChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME,
164 TestModel.ID_QNAME, 1)).build()).build();
166 writeTx.merge(TestModel.TEST_PATH, containerNode);
168 assertThreePhaseCommit(writeTx.ready());
170 Optional<NormalizedNode> afterCommitRead =
171 domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get();
172 assertEquals("After commit read: isPresent", true, afterCommitRead.isPresent());
173 assertEquals("After commit read: data", containerNode, afterCommitRead.get());
175 // Merge a new list entry node
177 writeTx = domStore.newWriteOnlyTransaction();
178 assertNotNull(writeTx);
180 containerNode = Builders.containerBuilder()
181 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
182 .addChild(ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
183 .addChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1))
184 .addChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2))
188 writeTx.merge(TestModel.TEST_PATH, containerNode);
190 assertThreePhaseCommit(writeTx.ready());
192 afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get();
193 assertEquals("After commit read: isPresent", true, afterCommitRead.isPresent());
194 assertEquals("After commit read: data", containerNode, afterCommitRead.get());
198 public void testExistsForExistingData() throws Exception {
200 DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
201 assertNotNull(writeTx);
203 ContainerNode containerNode = Builders.containerBuilder()
204 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
205 .addChild(ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
206 .addChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME,
207 TestModel.ID_QNAME, 1)).build()).build();
209 writeTx.merge(TestModel.TEST_PATH, containerNode);
211 ListenableFuture<Boolean> exists = writeTx.exists(TestModel.TEST_PATH);
213 assertEquals(Boolean.TRUE, exists.get());
215 DOMStoreThreePhaseCommitCohort ready = writeTx.ready();
217 ready.preCommit().get();
219 ready.commit().get();
221 DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
222 assertNotNull(readTx);
224 exists = readTx.exists(TestModel.TEST_PATH);
226 assertEquals(Boolean.TRUE, exists.get());
230 public void testExistsForNonExistingData() throws Exception {
232 DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
233 assertNotNull(writeTx);
235 var exists = writeTx.exists(TestModel.TEST_PATH);
237 assertEquals(Boolean.FALSE, exists.get());
239 var readTx = domStore.newReadOnlyTransaction();
240 assertNotNull(readTx);
242 exists = readTx.exists(TestModel.TEST_PATH);
244 assertEquals(Boolean.FALSE, exists.get());
248 public void testExistsThrowsReadFailedException() {
249 var readTx = domStore.newReadOnlyTransaction();
250 assertNotNull(readTx);
254 final var future = readTx.exists(TestModel.TEST_PATH);
256 final var ex = assertThrows(ExecutionException.class, future::get).getCause();
257 assertThat(ex, instanceOf(ReadFailedException.class));
262 public void testReadWithReadOnlyTransactionClosed() {
263 DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
264 assertNotNull(readTx);
268 assertReadThrows(readTx);
272 public void testReadWithReadOnlyTransactionFailure() {
273 DataTreeSnapshot mockSnapshot = mock(DataTreeSnapshot.class);
274 doThrow(new RuntimeException("mock ex")).when(mockSnapshot)
275 .readNode(any(YangInstanceIdentifier.class));
277 DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadTransaction("1", true, mockSnapshot);
279 assertReadThrows(readTx);
283 public void testReadWithReadWriteTransactionClosed() {
285 DOMStoreReadTransaction readTx = domStore.newReadWriteTransaction();
286 assertNotNull(readTx);
290 assertReadThrows(readTx);
294 public void testReadWithReadWriteTransactionFailure() {
295 DataTreeSnapshot mockSnapshot = mock(DataTreeSnapshot.class);
296 DataTreeModification mockModification = mock(DataTreeModification.class);
297 doThrow(new RuntimeException("mock ex")).when(mockModification)
298 .readNode(any(YangInstanceIdentifier.class));
299 doReturn(mockModification).when(mockSnapshot).newModification();
300 @SuppressWarnings("unchecked")
301 TransactionReadyPrototype<String> mockReady = mock(TransactionReadyPrototype.class);
302 DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadWriteTransaction(
303 "1", false, mockSnapshot, mockReady);
305 assertReadThrows(readTx);
308 private static void assertReadThrows(final DOMStoreReadTransaction readTx) {
309 final var future = readTx.read(TestModel.TEST_PATH);
310 final var cause = assertThrows(ExecutionException.class, future::get).getCause();
311 assertThat(cause, instanceOf(ReadFailedException.class));
315 public void testWriteWithTransactionReady() {
316 var writeTx = domStore.newWriteOnlyTransaction();
320 assertThrows(IllegalStateException.class,
321 () -> writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME)));
325 public void testReadyWithTransactionAlreadyReady() {
326 var writeTx = domStore.newWriteOnlyTransaction();
331 assertThrows(IllegalStateException.class, writeTx::ready);
335 public void testReadyWithMissingMandatoryData() throws Exception {
336 DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
337 NormalizedNode testNode = Builders.containerBuilder()
338 .withNodeIdentifier(new NodeIdentifier(TestModel.MANDATORY_DATA_TEST_QNAME))
339 .addChild(ImmutableNodes.leafNode(TestModel.OPTIONAL_QNAME, "data"))
341 writeTx.write(TestModel.MANDATORY_DATA_TEST_PATH, testNode);
342 DOMStoreThreePhaseCommitCohort ready = writeTx.ready();
344 final var future = ready.canCommit();
345 final var cause = assertThrows(ExecutionException.class, future::get).getCause();
346 assertThat(cause, instanceOf(IllegalArgumentException.class));
347 assertThat(cause.getMessage(), containsString("mandatory-data-test is missing mandatory descendant"));
351 public void testTransactionAbort() throws Exception {
353 DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
354 assertNotNull(writeTx);
356 assertTestContainerWrite(writeTx);
358 DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
360 assertTrue(cohort.canCommit().get());
361 cohort.preCommit().get();
362 cohort.abort().get();
364 assertEquals(Optional.empty(), domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get());
368 public void testTransactionChain() throws Exception {
369 DOMStoreTransactionChain txChain = domStore.createTransactionChain();
370 assertNotNull(txChain);
373 * We alocate new read-write transaction and write /test.
375 DOMStoreReadWriteTransaction firstTx = txChain.newReadWriteTransaction();
376 assertTestContainerWrite(firstTx);
379 * First transaction is marked as ready, we are able to allocate chained
382 final DOMStoreThreePhaseCommitCohort firstWriteTxCohort = firstTx.ready();
385 * We alocate chained transaction - read transaction, note first one is
386 * still not commited to datastore.
388 DOMStoreReadTransaction secondReadTx = txChain.newReadOnlyTransaction();
391 * We test if we are able to read data from tx, read should not fail
392 * since we are using chained transaction.
394 assertTestContainerExists(secondReadTx);
397 * We alocate next transaction, which is still based on first one, but
400 DOMStoreReadWriteTransaction thirdDeleteTx = txChain.newReadWriteTransaction();
403 * We test existence of /test in third transaction container should
404 * still be visible from first one (which is still uncommmited).
406 assertTestContainerExists(thirdDeleteTx);
409 * We delete node in third transaction.
411 thirdDeleteTx.delete(TestModel.TEST_PATH);
414 * third transaction is sealed.
416 DOMStoreThreePhaseCommitCohort thirdDeleteTxCohort = thirdDeleteTx.ready();
419 * We commit first transaction.
422 assertThreePhaseCommit(firstWriteTxCohort);
424 // Alocates store transacion
425 DOMStoreReadTransaction storeReadTx = domStore.newReadOnlyTransaction();
427 * We verify transaction is commited to store, container should exists
430 assertTestContainerExists(storeReadTx);
432 * We commit third transaction
435 assertThreePhaseCommit(thirdDeleteTxCohort);
440 public void testTransactionConflict() throws Exception {
441 DOMStoreReadWriteTransaction txOne = domStore.newReadWriteTransaction();
442 DOMStoreReadWriteTransaction txTwo = domStore.newReadWriteTransaction();
443 assertTestContainerWrite(txOne);
444 assertTestContainerWrite(txTwo);
447 * Commits transaction
449 assertThreePhaseCommit(txOne.ready());
452 * Asserts that txTwo could not be commited
454 assertFalse(txTwo.ready().canCommit().get());
457 private static void assertThreePhaseCommit(final DOMStoreThreePhaseCommitCohort cohort) throws Exception {
458 assertTrue(cohort.canCommit().get());
459 cohort.preCommit().get();
460 cohort.commit().get();
463 private static Optional<NormalizedNode> assertTestContainerWrite(final DOMStoreReadWriteTransaction writeTx)
467 * Writes /test in writeTx
470 writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
472 return assertTestContainerExists(writeTx);
476 * Reads /test from readTx Read should return container.
478 private static Optional<NormalizedNode> assertTestContainerExists(final DOMStoreReadTransaction readTx)
481 ListenableFuture<Optional<NormalizedNode>> writeTxContainer = readTx.read(TestModel.TEST_PATH);
482 assertTrue(writeTxContainer.get().isPresent());
483 return writeTxContainer.get();