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.schema.ContainerNode;
44 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
45 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
46 import org.opendaylight.yangtools.yang.data.impl.schema.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 NormalizedNode testNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
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)));
98 public void testTransactionCommit() throws Exception {
100 DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
101 assertNotNull(writeTx);
104 * Writes /test in writeTx.
106 NormalizedNode testNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
107 writeTx.write(TestModel.TEST_PATH, testNode);
110 * Reads /test from writeTx Read should return container.
112 assertEquals(Optional.of(testNode), Futures.getDone(writeTx.read(TestModel.TEST_PATH)));
114 DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
116 assertThreePhaseCommit(cohort);
118 assertEquals(Optional.of(testNode),
119 Futures.getDone(domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)));
123 public void testDelete() throws Exception {
125 DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
126 assertNotNull(writeTx);
128 // Write /test and commit
130 writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
132 assertThreePhaseCommit(writeTx.ready());
134 assertTrue(Futures.getDone(domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)).isPresent());
136 // Delete /test and verify
138 writeTx = domStore.newWriteOnlyTransaction();
140 writeTx.delete(TestModel.TEST_PATH);
142 assertThreePhaseCommit(writeTx.ready());
144 assertEquals(Optional.empty(), Futures.getDone(domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)));
148 public void testMerge() throws Exception {
150 DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
151 assertNotNull(writeTx);
153 ContainerNode containerNode = Builders.containerBuilder()
154 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
155 .addChild(ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
156 .addChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME,
157 TestModel.ID_QNAME, 1)).build()).build();
159 writeTx.merge(TestModel.TEST_PATH, containerNode);
161 assertThreePhaseCommit(writeTx.ready());
163 assertEquals(Optional.of(containerNode),
164 Futures.getDone(domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)));
166 // Merge a new list entry node
168 writeTx = domStore.newWriteOnlyTransaction();
169 assertNotNull(writeTx);
171 containerNode = Builders.containerBuilder()
172 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
173 .addChild(ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
174 .addChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1))
175 .addChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2))
179 writeTx.merge(TestModel.TEST_PATH, containerNode);
181 assertThreePhaseCommit(writeTx.ready());
183 assertEquals(Optional.of(containerNode),
184 Futures.getDone(domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)));
188 public void testExistsForExistingData() throws Exception {
190 DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
191 assertNotNull(writeTx);
193 ContainerNode containerNode = Builders.containerBuilder()
194 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
195 .addChild(ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
196 .addChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME,
197 TestModel.ID_QNAME, 1)).build()).build();
199 writeTx.merge(TestModel.TEST_PATH, containerNode);
201 assertEquals(Boolean.TRUE, Futures.getDone(writeTx.exists(TestModel.TEST_PATH)));
203 DOMStoreThreePhaseCommitCohort ready = writeTx.ready();
205 ready.preCommit().get();
207 ready.commit().get();
209 DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
210 assertNotNull(readTx);
212 assertEquals(Boolean.TRUE, Futures.getDone(readTx.exists(TestModel.TEST_PATH)));
216 public void testExistsForNonExistingData() throws Exception {
218 DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
219 assertNotNull(writeTx);
221 var exists = writeTx.exists(TestModel.TEST_PATH);
223 assertEquals(Boolean.FALSE, exists.get());
225 var readTx = domStore.newReadOnlyTransaction();
226 assertNotNull(readTx);
228 exists = readTx.exists(TestModel.TEST_PATH);
230 assertEquals(Boolean.FALSE, exists.get());
234 public void testExistsThrowsReadFailedException() {
235 var readTx = domStore.newReadOnlyTransaction();
236 assertNotNull(readTx);
240 final var future = readTx.exists(TestModel.TEST_PATH);
242 final var ex = assertThrows(ExecutionException.class, future::get).getCause();
243 assertThat(ex, instanceOf(ReadFailedException.class));
248 public void testReadWithReadOnlyTransactionClosed() {
249 DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
250 assertNotNull(readTx);
254 assertReadThrows(readTx);
258 public void testReadWithReadOnlyTransactionFailure() {
259 DataTreeSnapshot mockSnapshot = mock(DataTreeSnapshot.class);
260 doThrow(new RuntimeException("mock ex")).when(mockSnapshot)
261 .readNode(any(YangInstanceIdentifier.class));
263 DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadTransaction("1", true, mockSnapshot);
265 assertReadThrows(readTx);
269 public void testReadWithReadWriteTransactionClosed() {
271 DOMStoreReadTransaction readTx = domStore.newReadWriteTransaction();
272 assertNotNull(readTx);
276 assertReadThrows(readTx);
280 public void testReadWithReadWriteTransactionFailure() {
281 DataTreeSnapshot mockSnapshot = mock(DataTreeSnapshot.class);
282 DataTreeModification mockModification = mock(DataTreeModification.class);
283 doThrow(new RuntimeException("mock ex")).when(mockModification)
284 .readNode(any(YangInstanceIdentifier.class));
285 doReturn(mockModification).when(mockSnapshot).newModification();
286 @SuppressWarnings("unchecked")
287 TransactionReadyPrototype<String> mockReady = mock(TransactionReadyPrototype.class);
288 DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadWriteTransaction(
289 "1", false, mockSnapshot, mockReady);
291 assertReadThrows(readTx);
294 private static void assertReadThrows(final DOMStoreReadTransaction readTx) {
295 final var future = readTx.read(TestModel.TEST_PATH);
296 final var cause = assertThrows(ExecutionException.class, future::get).getCause();
297 assertThat(cause, instanceOf(ReadFailedException.class));
301 public void testWriteWithTransactionReady() {
302 var writeTx = domStore.newWriteOnlyTransaction();
306 assertThrows(IllegalStateException.class,
307 () -> writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME)));
311 public void testReadyWithTransactionAlreadyReady() {
312 var writeTx = domStore.newWriteOnlyTransaction();
317 assertThrows(IllegalStateException.class, writeTx::ready);
321 public void testReadyWithMissingMandatoryData() throws Exception {
322 DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
323 NormalizedNode testNode = Builders.containerBuilder()
324 .withNodeIdentifier(new NodeIdentifier(TestModel.MANDATORY_DATA_TEST_QNAME))
325 .addChild(ImmutableNodes.leafNode(TestModel.OPTIONAL_QNAME, "data"))
327 writeTx.write(TestModel.MANDATORY_DATA_TEST_PATH, testNode);
328 DOMStoreThreePhaseCommitCohort ready = writeTx.ready();
330 final var future = ready.canCommit();
331 final var cause = assertThrows(ExecutionException.class, future::get).getCause();
332 assertThat(cause, instanceOf(IllegalArgumentException.class));
333 assertThat(cause.getMessage(), containsString("mandatory-data-test is missing mandatory descendant"));
337 public void testTransactionAbort() throws Exception {
339 DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
340 assertNotNull(writeTx);
342 assertTestContainerWrite(writeTx);
344 DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
346 assertTrue(cohort.canCommit().get());
347 cohort.preCommit().get();
348 cohort.abort().get();
350 assertEquals(Optional.empty(), domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get());
354 public void testTransactionChain() throws Exception {
355 DOMStoreTransactionChain txChain = domStore.createTransactionChain();
356 assertNotNull(txChain);
359 * We alocate new read-write transaction and write /test.
361 DOMStoreReadWriteTransaction firstTx = txChain.newReadWriteTransaction();
362 assertTestContainerWrite(firstTx);
365 * First transaction is marked as ready, we are able to allocate chained
368 final DOMStoreThreePhaseCommitCohort firstWriteTxCohort = firstTx.ready();
371 * We alocate chained transaction - read transaction, note first one is
372 * still not commited to datastore.
374 DOMStoreReadTransaction secondReadTx = txChain.newReadOnlyTransaction();
377 * We test if we are able to read data from tx, read should not fail
378 * since we are using chained transaction.
380 assertTestContainerExists(secondReadTx);
383 * We alocate next transaction, which is still based on first one, but
386 DOMStoreReadWriteTransaction thirdDeleteTx = txChain.newReadWriteTransaction();
389 * We test existence of /test in third transaction container should
390 * still be visible from first one (which is still uncommmited).
392 assertTestContainerExists(thirdDeleteTx);
395 * We delete node in third transaction.
397 thirdDeleteTx.delete(TestModel.TEST_PATH);
400 * third transaction is sealed.
402 DOMStoreThreePhaseCommitCohort thirdDeleteTxCohort = thirdDeleteTx.ready();
405 * We commit first transaction.
408 assertThreePhaseCommit(firstWriteTxCohort);
410 // Alocates store transacion
411 DOMStoreReadTransaction storeReadTx = domStore.newReadOnlyTransaction();
413 * We verify transaction is commited to store, container should exists
416 assertTestContainerExists(storeReadTx);
418 * We commit third transaction
421 assertThreePhaseCommit(thirdDeleteTxCohort);
426 public void testTransactionConflict() throws Exception {
427 DOMStoreReadWriteTransaction txOne = domStore.newReadWriteTransaction();
428 DOMStoreReadWriteTransaction txTwo = domStore.newReadWriteTransaction();
429 assertTestContainerWrite(txOne);
430 assertTestContainerWrite(txTwo);
433 * Commits transaction
435 assertThreePhaseCommit(txOne.ready());
438 * Asserts that txTwo could not be commited
440 assertFalse(txTwo.ready().canCommit().get());
443 private static void assertThreePhaseCommit(final DOMStoreThreePhaseCommitCohort cohort) throws Exception {
444 assertTrue(cohort.canCommit().get());
445 cohort.preCommit().get();
446 cohort.commit().get();
449 private static Optional<NormalizedNode> assertTestContainerWrite(final DOMStoreReadWriteTransaction writeTx)
453 * Writes /test in writeTx
456 writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
458 return assertTestContainerExists(writeTx);
462 * Reads /test from readTx Read should return container.
464 private static Optional<NormalizedNode> assertTestContainerExists(final DOMStoreReadTransaction readTx)
467 ListenableFuture<Optional<NormalizedNode>> writeTxContainer = readTx.read(TestModel.TEST_PATH);
468 assertTrue(writeTxContainer.get().isPresent());
469 return writeTxContainer.get();