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.controller.md.sal.dom.store.impl;
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.base.Optional;
16 import com.google.common.util.concurrent.ListenableFuture;
17 import com.google.common.util.concurrent.MoreExecutors;
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.mdsal.dom.store.inmemory.InMemoryDOMDataStore;
33 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
34 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
35 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
36 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
37 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
38 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
39 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
40 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
41 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
43 public class InMemoryDataStoreTest {
45 private SchemaContext schemaContext;
46 private InMemoryDOMDataStore domStore;
49 public void setupStore() {
50 domStore = new InMemoryDOMDataStore("TEST", MoreExecutors.newDirectExecutorService());
51 schemaContext = TestModel.createTestContext();
52 domStore.onGlobalContextUpdated(schemaContext);
56 public void testTransactionIsolation() throws InterruptedException, ExecutionException {
58 assertNotNull(domStore);
60 DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
61 assertNotNull(readTx);
63 DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
64 assertNotNull(writeTx);
67 * Writes /test in writeTx.
69 NormalizedNode<?, ?> testNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
70 writeTx.write(TestModel.TEST_PATH, testNode);
73 * Reads /test from writeTx Read should return container.
75 ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(TestModel.TEST_PATH);
76 assertEquals("read: isPresent", true, writeTxContainer.get().isPresent());
77 assertEquals("read: data", testNode, writeTxContainer.get().get());
80 * Reads /test from readTx Read should return Absent.
82 ListenableFuture<Optional<NormalizedNode<?, ?>>> readTxContainer = readTx.read(TestModel.TEST_PATH);
83 assertEquals("read: isPresent", false, readTxContainer.get().isPresent());
87 public void testTransactionCommit() throws InterruptedException, ExecutionException {
89 DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
90 assertNotNull(writeTx);
93 * Writes /test in writeTx.
95 NormalizedNode<?, ?> testNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
96 writeTx.write(TestModel.TEST_PATH, testNode);
99 * Reads /test from writeTx Read should return container.
101 ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(TestModel.TEST_PATH);
102 assertEquals("read: isPresent", true, writeTxContainer.get().isPresent());
103 assertEquals("read: data", testNode, writeTxContainer.get().get());
105 DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
107 assertThreePhaseCommit(cohort);
109 Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)
111 assertEquals("After commit read: isPresent", true, afterCommitRead.isPresent());
112 assertEquals("After commit read: data", testNode, afterCommitRead.get());
116 public void testDelete() throws Exception {
118 DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
119 assertNotNull(writeTx);
121 // Write /test and commit
123 writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
125 assertThreePhaseCommit(writeTx.ready());
127 Optional<NormalizedNode<?, ?>> afterCommitRead =
128 domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get();
129 assertEquals("After commit read: isPresent", true, afterCommitRead.isPresent());
131 // Delete /test and verify
133 writeTx = domStore.newWriteOnlyTransaction();
135 writeTx.delete(TestModel.TEST_PATH);
137 assertThreePhaseCommit(writeTx.ready());
139 afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get();
140 assertEquals("After commit read: isPresent", false, afterCommitRead.isPresent());
144 public void testMerge() throws Exception {
146 DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
147 assertNotNull(writeTx);
149 ContainerNode containerNode = ImmutableContainerNodeBuilder.create()
150 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
151 .addChild(ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
152 .addChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME,
153 TestModel.ID_QNAME, 1)).build()).build();
155 writeTx.merge(TestModel.TEST_PATH, containerNode);
157 assertThreePhaseCommit(writeTx.ready());
159 Optional<NormalizedNode<?, ?>> afterCommitRead =
160 domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get();
161 assertEquals("After commit read: isPresent", true, afterCommitRead.isPresent());
162 assertEquals("After commit read: data", containerNode, afterCommitRead.get());
164 // Merge a new list entry node
166 writeTx = domStore.newWriteOnlyTransaction();
167 assertNotNull(writeTx);
169 containerNode = ImmutableContainerNodeBuilder.create()
170 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
171 .addChild(ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
172 .addChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME,
173 TestModel.ID_QNAME, 1))
174 .addChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME,
175 TestModel.ID_QNAME, 2)).build()).build();
177 writeTx.merge(TestModel.TEST_PATH, containerNode);
179 assertThreePhaseCommit(writeTx.ready());
181 afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get();
182 assertEquals("After commit read: isPresent", true, afterCommitRead.isPresent());
183 assertEquals("After commit read: data", containerNode, afterCommitRead.get());
188 public void testExistsForExistingData() throws Exception {
190 DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
191 assertNotNull(writeTx);
193 ContainerNode containerNode = ImmutableContainerNodeBuilder.create()
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 ListenableFuture<Boolean> exists = writeTx.exists(TestModel.TEST_PATH);
203 assertEquals(Boolean.TRUE, exists.get());
205 DOMStoreThreePhaseCommitCohort ready = writeTx.ready();
207 ready.preCommit().get();
209 ready.commit().get();
211 DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
212 assertNotNull(readTx);
215 readTx.exists(TestModel.TEST_PATH);
217 assertEquals(Boolean.TRUE, exists.get());
221 public void testExistsForNonExistingData() throws Exception {
223 DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
224 assertNotNull(writeTx);
226 ListenableFuture<Boolean> exists = writeTx.exists(TestModel.TEST_PATH);
228 assertEquals(Boolean.FALSE, exists.get());
230 DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
231 assertNotNull(readTx);
234 readTx.exists(TestModel.TEST_PATH);
236 assertEquals(Boolean.FALSE, exists.get());
239 @Test(expected = ReadFailedException.class)
240 public void testExistsThrowsReadFailedException() throws Exception {
242 DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
243 assertNotNull(readTx);
247 readTx.exists(TestModel.TEST_PATH).checkedGet();
251 @SuppressWarnings("checkstyle:IllegalThrows")
252 @Test(expected = ReadFailedException.class)
253 public void testReadWithReadOnlyTransactionClosed() throws Throwable {
255 DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
256 assertNotNull(readTx);
260 doReadAndThrowEx(readTx);
263 @SuppressWarnings("checkstyle:IllegalThrows")
264 @Test(expected = ReadFailedException.class)
265 public void testReadWithReadOnlyTransactionFailure() throws Throwable {
267 DataTreeSnapshot mockSnapshot = Mockito.mock(DataTreeSnapshot.class);
268 Mockito.doThrow(new RuntimeException("mock ex")).when(mockSnapshot)
269 .readNode(Mockito.any(YangInstanceIdentifier.class));
271 DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadTransaction("1", true, mockSnapshot);
273 doReadAndThrowEx(readTx);
276 @SuppressWarnings("checkstyle:IllegalThrows")
277 @Test(expected = ReadFailedException.class)
278 public void testReadWithReadWriteTransactionClosed() throws Throwable {
280 DOMStoreReadTransaction readTx = domStore.newReadWriteTransaction();
281 assertNotNull(readTx);
285 doReadAndThrowEx(readTx);
288 @SuppressWarnings("checkstyle:IllegalThrows")
289 @Test(expected = ReadFailedException.class)
290 public void testReadWithReadWriteTransactionFailure() throws Throwable {
292 DataTreeSnapshot mockSnapshot = Mockito.mock(DataTreeSnapshot.class);
293 DataTreeModification mockModification = Mockito.mock(DataTreeModification.class);
294 Mockito.doThrow(new RuntimeException("mock ex")).when(mockModification)
295 .readNode(Mockito.any(YangInstanceIdentifier.class));
296 Mockito.doReturn(mockModification).when(mockSnapshot).newModification();
297 @SuppressWarnings("unchecked")
298 TransactionReadyPrototype<String> mockReady = Mockito.mock(TransactionReadyPrototype.class);
299 DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadWriteTransaction(
300 "1", false, mockSnapshot, mockReady);
302 doReadAndThrowEx(readTx);
305 @SuppressWarnings({ "checkstyle:IllegalThrows", "checkstyle:avoidHidingCauseException" })
306 private static void doReadAndThrowEx(final DOMStoreReadTransaction readTx) throws Throwable {
308 readTx.read(TestModel.TEST_PATH).get();
309 } catch (ExecutionException e) {
314 @Test(expected = IllegalStateException.class)
315 public void testWriteWithTransactionReady() throws Exception {
317 DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
322 writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
325 @Test(expected = IllegalStateException.class)
326 public void testReadyWithTransactionAlreadyReady() throws Exception {
328 DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
337 public void testReadyWithMissingMandatoryData() throws InterruptedException {
338 DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
339 NormalizedNode<?, ?> testNode = ImmutableContainerNodeBuilder.create()
340 .withNodeIdentifier(new NodeIdentifier(TestModel.MANDATORY_DATA_TEST_QNAME))
341 .addChild(ImmutableNodes.leafNode(TestModel.OPTIONAL_QNAME, "data"))
343 writeTx.write(TestModel.MANDATORY_DATA_TEST_PATH, testNode);
344 DOMStoreThreePhaseCommitCohort ready = writeTx.ready();
346 ready.canCommit().get();
347 Assert.fail("Expected exception on canCommit");
348 } catch (ExecutionException e) {
354 public void testTransactionAbort() throws InterruptedException, ExecutionException {
356 DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
357 assertNotNull(writeTx);
359 assertTestContainerWrite(writeTx);
361 DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
363 assertTrue(cohort.canCommit().get().booleanValue());
364 cohort.preCommit().get();
365 cohort.abort().get();
367 Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)
369 assertFalse(afterCommitRead.isPresent());
373 public void testTransactionChain() throws InterruptedException, ExecutionException {
374 DOMStoreTransactionChain txChain = domStore.createTransactionChain();
375 assertNotNull(txChain);
378 * We alocate new read-write transaction and write /test.
380 DOMStoreReadWriteTransaction firstTx = txChain.newReadWriteTransaction();
381 assertTestContainerWrite(firstTx);
384 * First transaction is marked as ready, we are able to allocate chained
387 final DOMStoreThreePhaseCommitCohort firstWriteTxCohort = firstTx.ready();
390 * We alocate chained transaction - read transaction, note first one is
391 * still not commited to datastore.
393 DOMStoreReadTransaction secondReadTx = txChain.newReadOnlyTransaction();
396 * We test if we are able to read data from tx, read should not fail
397 * since we are using chained transaction.
399 assertTestContainerExists(secondReadTx);
402 * We alocate next transaction, which is still based on first one, but
405 DOMStoreReadWriteTransaction thirdDeleteTx = txChain.newReadWriteTransaction();
408 * We test existence of /test in third transaction container should
409 * still be visible from first one (which is still uncommmited).
411 assertTestContainerExists(thirdDeleteTx);
414 * We delete node in third transaction.
416 thirdDeleteTx.delete(TestModel.TEST_PATH);
419 * third transaction is sealed.
421 DOMStoreThreePhaseCommitCohort thirdDeleteTxCohort = thirdDeleteTx.ready();
424 * We commit first transaction.
427 assertThreePhaseCommit(firstWriteTxCohort);
429 // Alocates store transacion
430 DOMStoreReadTransaction storeReadTx = domStore.newReadOnlyTransaction();
432 * We verify transaction is commited to store, container should exists
435 assertTestContainerExists(storeReadTx);
437 * We commit third transaction
440 assertThreePhaseCommit(thirdDeleteTxCohort);
445 public void testTransactionConflict() throws InterruptedException, ExecutionException {
446 DOMStoreReadWriteTransaction txOne = domStore.newReadWriteTransaction();
447 DOMStoreReadWriteTransaction txTwo = domStore.newReadWriteTransaction();
448 assertTestContainerWrite(txOne);
449 assertTestContainerWrite(txTwo);
452 * Commits transaction
454 assertThreePhaseCommit(txOne.ready());
457 * Asserts that txTwo could not be commited
459 assertFalse(txTwo.ready().canCommit().get());
462 private static void assertThreePhaseCommit(final DOMStoreThreePhaseCommitCohort cohort)
463 throws InterruptedException, ExecutionException {
464 assertTrue(cohort.canCommit().get().booleanValue());
465 cohort.preCommit().get();
466 cohort.commit().get();
469 private static Optional<NormalizedNode<?, ?>> assertTestContainerWrite(final DOMStoreReadWriteTransaction writeTx)
470 throws InterruptedException, ExecutionException {
473 * Writes /test in writeTx
476 writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
478 return assertTestContainerExists(writeTx);
482 * Reads /test from readTx Read should return container.
484 private static Optional<NormalizedNode<?, ?>> assertTestContainerExists(final DOMStoreReadTransaction readTx)
485 throws InterruptedException, ExecutionException {
487 ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = readTx.read(TestModel.TEST_PATH);
488 assertTrue(writeTxContainer.get().isPresent());
489 return writeTxContainer.get();