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.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.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 @SuppressWarnings({"checkstyle:IllegalThrows", "checkstyle:AvoidHidingCauseException"})
241 public void testExistsThrowsReadFailedException() throws Throwable {
243 DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
244 assertNotNull(readTx);
249 readTx.exists(TestModel.TEST_PATH).get();
250 } catch (ExecutionException e) {
256 @SuppressWarnings("checkstyle:IllegalThrows")
257 @Test(expected = ReadFailedException.class)
258 public void testReadWithReadOnlyTransactionClosed() throws Throwable {
260 DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
261 assertNotNull(readTx);
265 doReadAndThrowEx(readTx);
268 @SuppressWarnings("checkstyle:IllegalThrows")
269 @Test(expected = ReadFailedException.class)
270 public void testReadWithReadOnlyTransactionFailure() throws Throwable {
272 DataTreeSnapshot mockSnapshot = Mockito.mock(DataTreeSnapshot.class);
273 Mockito.doThrow(new RuntimeException("mock ex")).when(mockSnapshot)
274 .readNode(Mockito.any(YangInstanceIdentifier.class));
276 DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadTransaction("1", true, mockSnapshot);
278 doReadAndThrowEx(readTx);
281 @SuppressWarnings("checkstyle:IllegalThrows")
282 @Test(expected = ReadFailedException.class)
283 public void testReadWithReadWriteTransactionClosed() throws Throwable {
285 DOMStoreReadTransaction readTx = domStore.newReadWriteTransaction();
286 assertNotNull(readTx);
290 doReadAndThrowEx(readTx);
293 @SuppressWarnings("checkstyle:IllegalThrows")
294 @Test(expected = ReadFailedException.class)
295 public void testReadWithReadWriteTransactionFailure() throws Throwable {
297 DataTreeSnapshot mockSnapshot = Mockito.mock(DataTreeSnapshot.class);
298 DataTreeModification mockModification = Mockito.mock(DataTreeModification.class);
299 Mockito.doThrow(new RuntimeException("mock ex")).when(mockModification)
300 .readNode(Mockito.any(YangInstanceIdentifier.class));
301 Mockito.doReturn(mockModification).when(mockSnapshot).newModification();
302 @SuppressWarnings("unchecked")
303 TransactionReadyPrototype<String> mockReady = Mockito.mock(TransactionReadyPrototype.class);
304 DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadWriteTransaction(
305 "1", false, mockSnapshot, mockReady);
307 doReadAndThrowEx(readTx);
310 @SuppressWarnings({ "checkstyle:IllegalThrows", "checkstyle:avoidHidingCauseException" })
311 private static void doReadAndThrowEx(final DOMStoreReadTransaction readTx) throws Throwable {
313 readTx.read(TestModel.TEST_PATH).get();
314 } catch (ExecutionException e) {
319 @Test(expected = IllegalStateException.class)
320 public void testWriteWithTransactionReady() throws Exception {
322 DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
327 writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
330 @Test(expected = IllegalStateException.class)
331 public void testReadyWithTransactionAlreadyReady() throws Exception {
333 DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
342 public void testReadyWithMissingMandatoryData() throws InterruptedException {
343 DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
344 NormalizedNode<?, ?> testNode = ImmutableContainerNodeBuilder.create()
345 .withNodeIdentifier(new NodeIdentifier(TestModel.MANDATORY_DATA_TEST_QNAME))
346 .addChild(ImmutableNodes.leafNode(TestModel.OPTIONAL_QNAME, "data"))
348 writeTx.write(TestModel.MANDATORY_DATA_TEST_PATH, testNode);
349 DOMStoreThreePhaseCommitCohort ready = writeTx.ready();
351 ready.canCommit().get();
352 Assert.fail("Expected exception on canCommit");
353 } catch (ExecutionException e) {
359 public void testTransactionAbort() throws InterruptedException, ExecutionException {
361 DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
362 assertNotNull(writeTx);
364 assertTestContainerWrite(writeTx);
366 DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
368 assertTrue(cohort.canCommit().get().booleanValue());
369 cohort.preCommit().get();
370 cohort.abort().get();
372 Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)
374 assertFalse(afterCommitRead.isPresent());
378 public void testTransactionChain() throws InterruptedException, ExecutionException {
379 DOMStoreTransactionChain txChain = domStore.createTransactionChain();
380 assertNotNull(txChain);
383 * We alocate new read-write transaction and write /test.
385 DOMStoreReadWriteTransaction firstTx = txChain.newReadWriteTransaction();
386 assertTestContainerWrite(firstTx);
389 * First transaction is marked as ready, we are able to allocate chained
392 final DOMStoreThreePhaseCommitCohort firstWriteTxCohort = firstTx.ready();
395 * We alocate chained transaction - read transaction, note first one is
396 * still not commited to datastore.
398 DOMStoreReadTransaction secondReadTx = txChain.newReadOnlyTransaction();
401 * We test if we are able to read data from tx, read should not fail
402 * since we are using chained transaction.
404 assertTestContainerExists(secondReadTx);
407 * We alocate next transaction, which is still based on first one, but
410 DOMStoreReadWriteTransaction thirdDeleteTx = txChain.newReadWriteTransaction();
413 * We test existence of /test in third transaction container should
414 * still be visible from first one (which is still uncommmited).
416 assertTestContainerExists(thirdDeleteTx);
419 * We delete node in third transaction.
421 thirdDeleteTx.delete(TestModel.TEST_PATH);
424 * third transaction is sealed.
426 DOMStoreThreePhaseCommitCohort thirdDeleteTxCohort = thirdDeleteTx.ready();
429 * We commit first transaction.
432 assertThreePhaseCommit(firstWriteTxCohort);
434 // Alocates store transacion
435 DOMStoreReadTransaction storeReadTx = domStore.newReadOnlyTransaction();
437 * We verify transaction is commited to store, container should exists
440 assertTestContainerExists(storeReadTx);
442 * We commit third transaction
445 assertThreePhaseCommit(thirdDeleteTxCohort);
450 public void testTransactionConflict() throws InterruptedException, ExecutionException {
451 DOMStoreReadWriteTransaction txOne = domStore.newReadWriteTransaction();
452 DOMStoreReadWriteTransaction txTwo = domStore.newReadWriteTransaction();
453 assertTestContainerWrite(txOne);
454 assertTestContainerWrite(txTwo);
457 * Commits transaction
459 assertThreePhaseCommit(txOne.ready());
462 * Asserts that txTwo could not be commited
464 assertFalse(txTwo.ready().canCommit().get());
467 private static void assertThreePhaseCommit(final DOMStoreThreePhaseCommitCohort cohort)
468 throws InterruptedException, ExecutionException {
469 assertTrue(cohort.canCommit().get().booleanValue());
470 cohort.preCommit().get();
471 cohort.commit().get();
474 private static Optional<NormalizedNode<?, ?>> assertTestContainerWrite(final DOMStoreReadWriteTransaction writeTx)
475 throws InterruptedException, ExecutionException {
478 * Writes /test in writeTx
481 writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
483 return assertTestContainerExists(writeTx);
487 * Reads /test from readTx Read should return container.
489 private static Optional<NormalizedNode<?, ?>> assertTestContainerExists(final DOMStoreReadTransaction readTx)
490 throws InterruptedException, ExecutionException {
492 ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = readTx.read(TestModel.TEST_PATH);
493 assertTrue(writeTxContainer.get().isPresent());
494 return writeTxContainer.get();