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.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertFalse;
12 import static org.junit.Assert.assertNotNull;
13 import static org.junit.Assert.assertTrue;
15 import com.google.common.util.concurrent.ListenableFuture;
16 import com.google.common.util.concurrent.MoreExecutors;
17 import java.util.Optional;
18 import java.util.concurrent.ExecutionException;
19 import org.junit.Assert;
20 import org.junit.Before;
21 import org.junit.Ignore;
22 import org.junit.Test;
23 import org.mockito.Mockito;
24 import org.opendaylight.mdsal.common.api.ReadFailedException;
25 import org.opendaylight.mdsal.dom.spi.store.DOMStoreReadTransaction;
26 import org.opendaylight.mdsal.dom.spi.store.DOMStoreReadWriteTransaction;
27 import org.opendaylight.mdsal.dom.spi.store.DOMStoreThreePhaseCommitCohort;
28 import org.opendaylight.mdsal.dom.spi.store.DOMStoreTransactionChain;
29 import org.opendaylight.mdsal.dom.spi.store.DOMStoreWriteTransaction;
30 import org.opendaylight.mdsal.dom.spi.store.SnapshotBackedTransactions;
31 import org.opendaylight.mdsal.dom.spi.store.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
32 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
33 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
34 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
35 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
36 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
37 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
38 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
39 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
40 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
42 public class InMemoryDataStoreTest {
44 private SchemaContext schemaContext;
45 private InMemoryDOMDataStore domStore;
48 public void setupStore() {
49 domStore = new InMemoryDOMDataStore("TEST", MoreExecutors.newDirectExecutorService());
50 schemaContext = TestModel.createTestContext();
51 domStore.onGlobalContextUpdated(schemaContext);
55 public void testTransactionIsolation() throws InterruptedException, ExecutionException {
57 assertNotNull(domStore);
59 DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
60 assertNotNull(readTx);
62 DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
63 assertNotNull(writeTx);
66 * Writes /test in writeTx.
68 NormalizedNode<?, ?> testNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
69 writeTx.write(TestModel.TEST_PATH, testNode);
72 * Reads /test from writeTx Read should return container.
74 ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(TestModel.TEST_PATH);
75 assertEquals("read: isPresent", true, writeTxContainer.get().isPresent());
76 assertEquals("read: data", testNode, writeTxContainer.get().get());
79 * Reads /test from readTx Read should return Absent.
81 ListenableFuture<Optional<NormalizedNode<?, ?>>> readTxContainer = readTx.read(TestModel.TEST_PATH);
82 assertEquals("read: isPresent", false, readTxContainer.get().isPresent());
86 public void testTransactionCommit() throws InterruptedException, ExecutionException {
88 DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
89 assertNotNull(writeTx);
92 * Writes /test in writeTx.
94 NormalizedNode<?, ?> testNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
95 writeTx.write(TestModel.TEST_PATH, testNode);
98 * Reads /test from writeTx Read should return container.
100 ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(TestModel.TEST_PATH);
101 assertEquals("read: isPresent", true, writeTxContainer.get().isPresent());
102 assertEquals("read: data", testNode, writeTxContainer.get().get());
104 DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
106 assertThreePhaseCommit(cohort);
108 Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)
110 assertEquals("After commit read: isPresent", true, afterCommitRead.isPresent());
111 assertEquals("After commit read: data", testNode, afterCommitRead.get());
115 public void testDelete() throws Exception {
117 DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
118 assertNotNull(writeTx);
120 // Write /test and commit
122 writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
124 assertThreePhaseCommit(writeTx.ready());
126 Optional<NormalizedNode<?, ?>> afterCommitRead =
127 domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get();
128 assertEquals("After commit read: isPresent", true, afterCommitRead.isPresent());
130 // Delete /test and verify
132 writeTx = domStore.newWriteOnlyTransaction();
134 writeTx.delete(TestModel.TEST_PATH);
136 assertThreePhaseCommit(writeTx.ready());
138 afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get();
139 assertEquals("After commit read: isPresent", false, afterCommitRead.isPresent());
143 public void testMerge() throws Exception {
145 DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
146 assertNotNull(writeTx);
148 ContainerNode containerNode = ImmutableContainerNodeBuilder.create()
149 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
150 .addChild(ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
151 .addChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME,
152 TestModel.ID_QNAME, 1)).build()).build();
154 writeTx.merge(TestModel.TEST_PATH, containerNode);
156 assertThreePhaseCommit(writeTx.ready());
158 Optional<NormalizedNode<?, ?>> afterCommitRead =
159 domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get();
160 assertEquals("After commit read: isPresent", true, afterCommitRead.isPresent());
161 assertEquals("After commit read: data", containerNode, afterCommitRead.get());
163 // Merge a new list entry node
165 writeTx = domStore.newWriteOnlyTransaction();
166 assertNotNull(writeTx);
168 containerNode = ImmutableContainerNodeBuilder.create()
169 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
170 .addChild(ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
171 .addChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME,
172 TestModel.ID_QNAME, 1))
173 .addChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME,
174 TestModel.ID_QNAME, 2)).build()).build();
176 writeTx.merge(TestModel.TEST_PATH, containerNode);
178 assertThreePhaseCommit(writeTx.ready());
180 afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get();
181 assertEquals("After commit read: isPresent", true, afterCommitRead.isPresent());
182 assertEquals("After commit read: data", containerNode, afterCommitRead.get());
187 public void testExistsForExistingData() throws Exception {
189 DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
190 assertNotNull(writeTx);
192 ContainerNode containerNode = ImmutableContainerNodeBuilder.create()
193 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
194 .addChild(ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
195 .addChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME,
196 TestModel.ID_QNAME, 1)).build()).build();
198 writeTx.merge(TestModel.TEST_PATH, containerNode);
200 ListenableFuture<Boolean> exists = writeTx.exists(TestModel.TEST_PATH);
202 assertEquals(Boolean.TRUE, exists.get());
204 DOMStoreThreePhaseCommitCohort ready = writeTx.ready();
206 ready.preCommit().get();
208 ready.commit().get();
210 DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
211 assertNotNull(readTx);
214 readTx.exists(TestModel.TEST_PATH);
216 assertEquals(Boolean.TRUE, exists.get());
220 public void testExistsForNonExistingData() throws Exception {
222 DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
223 assertNotNull(writeTx);
225 ListenableFuture<Boolean> exists = writeTx.exists(TestModel.TEST_PATH);
227 assertEquals(Boolean.FALSE, exists.get());
229 DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
230 assertNotNull(readTx);
233 readTx.exists(TestModel.TEST_PATH);
235 assertEquals(Boolean.FALSE, exists.get());
238 @Test(expected = ReadFailedException.class)
239 @SuppressWarnings({"checkstyle:IllegalThrows", "checkstyle:AvoidHidingCauseException"})
240 public void testExistsThrowsReadFailedException() throws Throwable {
242 DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
243 assertNotNull(readTx);
248 readTx.exists(TestModel.TEST_PATH).get();
249 } catch (ExecutionException e) {
255 @SuppressWarnings("checkstyle:IllegalThrows")
256 @Test(expected = ReadFailedException.class)
257 public void testReadWithReadOnlyTransactionClosed() throws Throwable {
259 DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
260 assertNotNull(readTx);
264 doReadAndThrowEx(readTx);
267 @SuppressWarnings("checkstyle:IllegalThrows")
268 @Test(expected = ReadFailedException.class)
269 public void testReadWithReadOnlyTransactionFailure() throws Throwable {
271 DataTreeSnapshot mockSnapshot = Mockito.mock(DataTreeSnapshot.class);
272 Mockito.doThrow(new RuntimeException("mock ex")).when(mockSnapshot)
273 .readNode(Mockito.any(YangInstanceIdentifier.class));
275 DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadTransaction("1", true, mockSnapshot);
277 doReadAndThrowEx(readTx);
280 @SuppressWarnings("checkstyle:IllegalThrows")
281 @Test(expected = ReadFailedException.class)
282 public void testReadWithReadWriteTransactionClosed() throws Throwable {
284 DOMStoreReadTransaction readTx = domStore.newReadWriteTransaction();
285 assertNotNull(readTx);
289 doReadAndThrowEx(readTx);
292 @SuppressWarnings("checkstyle:IllegalThrows")
293 @Test(expected = ReadFailedException.class)
294 public void testReadWithReadWriteTransactionFailure() throws Throwable {
296 DataTreeSnapshot mockSnapshot = Mockito.mock(DataTreeSnapshot.class);
297 DataTreeModification mockModification = Mockito.mock(DataTreeModification.class);
298 Mockito.doThrow(new RuntimeException("mock ex")).when(mockModification)
299 .readNode(Mockito.any(YangInstanceIdentifier.class));
300 Mockito.doReturn(mockModification).when(mockSnapshot).newModification();
301 @SuppressWarnings("unchecked")
302 TransactionReadyPrototype<String> mockReady = Mockito.mock(TransactionReadyPrototype.class);
303 DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadWriteTransaction(
304 "1", false, mockSnapshot, mockReady);
306 doReadAndThrowEx(readTx);
309 @SuppressWarnings({ "checkstyle:IllegalThrows", "checkstyle:avoidHidingCauseException" })
310 private static void doReadAndThrowEx(final DOMStoreReadTransaction readTx) throws Throwable {
312 readTx.read(TestModel.TEST_PATH).get();
313 } catch (ExecutionException e) {
318 @Test(expected = IllegalStateException.class)
319 public void testWriteWithTransactionReady() throws Exception {
321 DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
326 writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
329 @Test(expected = IllegalStateException.class)
330 public void testReadyWithTransactionAlreadyReady() throws Exception {
332 DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
341 public void testReadyWithMissingMandatoryData() throws InterruptedException {
342 DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
343 NormalizedNode<?, ?> testNode = ImmutableContainerNodeBuilder.create()
344 .withNodeIdentifier(new NodeIdentifier(TestModel.MANDATORY_DATA_TEST_QNAME))
345 .addChild(ImmutableNodes.leafNode(TestModel.OPTIONAL_QNAME, "data"))
347 writeTx.write(TestModel.MANDATORY_DATA_TEST_PATH, testNode);
348 DOMStoreThreePhaseCommitCohort ready = writeTx.ready();
350 ready.canCommit().get();
351 Assert.fail("Expected exception on canCommit");
352 } catch (ExecutionException e) {
358 public void testTransactionAbort() throws InterruptedException, ExecutionException {
360 DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
361 assertNotNull(writeTx);
363 assertTestContainerWrite(writeTx);
365 DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
367 assertTrue(cohort.canCommit().get().booleanValue());
368 cohort.preCommit().get();
369 cohort.abort().get();
371 Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)
373 assertFalse(afterCommitRead.isPresent());
377 public void testTransactionChain() throws InterruptedException, ExecutionException {
378 DOMStoreTransactionChain txChain = domStore.createTransactionChain();
379 assertNotNull(txChain);
382 * We alocate new read-write transaction and write /test.
384 DOMStoreReadWriteTransaction firstTx = txChain.newReadWriteTransaction();
385 assertTestContainerWrite(firstTx);
388 * First transaction is marked as ready, we are able to allocate chained
391 final DOMStoreThreePhaseCommitCohort firstWriteTxCohort = firstTx.ready();
394 * We alocate chained transaction - read transaction, note first one is
395 * still not commited to datastore.
397 DOMStoreReadTransaction secondReadTx = txChain.newReadOnlyTransaction();
400 * We test if we are able to read data from tx, read should not fail
401 * since we are using chained transaction.
403 assertTestContainerExists(secondReadTx);
406 * We alocate next transaction, which is still based on first one, but
409 DOMStoreReadWriteTransaction thirdDeleteTx = txChain.newReadWriteTransaction();
412 * We test existence of /test in third transaction container should
413 * still be visible from first one (which is still uncommmited).
415 assertTestContainerExists(thirdDeleteTx);
418 * We delete node in third transaction.
420 thirdDeleteTx.delete(TestModel.TEST_PATH);
423 * third transaction is sealed.
425 DOMStoreThreePhaseCommitCohort thirdDeleteTxCohort = thirdDeleteTx.ready();
428 * We commit first transaction.
431 assertThreePhaseCommit(firstWriteTxCohort);
433 // Alocates store transacion
434 DOMStoreReadTransaction storeReadTx = domStore.newReadOnlyTransaction();
436 * We verify transaction is commited to store, container should exists
439 assertTestContainerExists(storeReadTx);
441 * We commit third transaction
444 assertThreePhaseCommit(thirdDeleteTxCohort);
449 public void testTransactionConflict() throws InterruptedException, ExecutionException {
450 DOMStoreReadWriteTransaction txOne = domStore.newReadWriteTransaction();
451 DOMStoreReadWriteTransaction txTwo = domStore.newReadWriteTransaction();
452 assertTestContainerWrite(txOne);
453 assertTestContainerWrite(txTwo);
456 * Commits transaction
458 assertThreePhaseCommit(txOne.ready());
461 * Asserts that txTwo could not be commited
463 assertFalse(txTwo.ready().canCommit().get());
466 private static void assertThreePhaseCommit(final DOMStoreThreePhaseCommitCohort cohort)
467 throws InterruptedException, ExecutionException {
468 assertTrue(cohort.canCommit().get().booleanValue());
469 cohort.preCommit().get();
470 cohort.commit().get();
473 private static Optional<NormalizedNode<?, ?>> assertTestContainerWrite(final DOMStoreReadWriteTransaction writeTx)
474 throws InterruptedException, ExecutionException {
477 * Writes /test in writeTx
480 writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
482 return assertTestContainerExists(writeTx);
486 * Reads /test from readTx Read should return container.
488 private static Optional<NormalizedNode<?, ?>> assertTestContainerExists(final DOMStoreReadTransaction readTx)
489 throws InterruptedException, ExecutionException {
491 ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = readTx.read(TestModel.TEST_PATH);
492 assertTrue(writeTxContainer.get().isPresent());
493 return writeTxContainer.get();