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.AfterClass;
20 import org.junit.Assert;
21 import org.junit.Before;
22 import org.junit.BeforeClass;
23 import org.junit.Ignore;
24 import org.junit.Test;
25 import org.mockito.Mockito;
26 import org.opendaylight.mdsal.common.api.ReadFailedException;
27 import org.opendaylight.mdsal.dom.spi.store.DOMStoreReadTransaction;
28 import org.opendaylight.mdsal.dom.spi.store.DOMStoreReadWriteTransaction;
29 import org.opendaylight.mdsal.dom.spi.store.DOMStoreThreePhaseCommitCohort;
30 import org.opendaylight.mdsal.dom.spi.store.DOMStoreTransactionChain;
31 import org.opendaylight.mdsal.dom.spi.store.DOMStoreWriteTransaction;
32 import org.opendaylight.mdsal.dom.spi.store.SnapshotBackedTransactions;
33 import org.opendaylight.mdsal.dom.spi.store.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
34 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
35 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
36 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
37 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
38 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
39 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
40 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
41 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
42 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
44 public class InMemoryDataStoreTest {
45 private static EffectiveModelContext SCHEMA_CONTEXT;
47 private InMemoryDOMDataStore domStore;
50 public static void beforeClass() {
51 SCHEMA_CONTEXT = TestModel.createTestContext();
55 public static void afterClass() {
56 SCHEMA_CONTEXT = null;
60 public void setupStore() {
61 domStore = new InMemoryDOMDataStore("TEST", MoreExecutors.newDirectExecutorService());
62 domStore.onModelContextUpdated(SCHEMA_CONTEXT);
66 public void testTransactionIsolation() throws InterruptedException, ExecutionException {
68 assertNotNull(domStore);
70 DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
71 assertNotNull(readTx);
73 DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
74 assertNotNull(writeTx);
77 * Writes /test in writeTx.
79 NormalizedNode testNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
80 writeTx.write(TestModel.TEST_PATH, testNode);
83 * Reads /test from writeTx Read should return container.
85 ListenableFuture<Optional<NormalizedNode>> writeTxContainer = writeTx.read(TestModel.TEST_PATH);
86 assertEquals("read: isPresent", true, writeTxContainer.get().isPresent());
87 assertEquals("read: data", testNode, writeTxContainer.get().get());
90 * Reads /test from readTx Read should return Absent.
92 ListenableFuture<Optional<NormalizedNode>> readTxContainer = readTx.read(TestModel.TEST_PATH);
93 assertEquals("read: isPresent", false, readTxContainer.get().isPresent());
97 public void testTransactionCommit() throws InterruptedException, ExecutionException {
99 DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
100 assertNotNull(writeTx);
103 * Writes /test in writeTx.
105 NormalizedNode testNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
106 writeTx.write(TestModel.TEST_PATH, testNode);
109 * Reads /test from writeTx Read should return container.
111 ListenableFuture<Optional<NormalizedNode>> writeTxContainer = writeTx.read(TestModel.TEST_PATH);
112 assertEquals("read: isPresent", true, writeTxContainer.get().isPresent());
113 assertEquals("read: data", testNode, writeTxContainer.get().get());
115 DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
117 assertThreePhaseCommit(cohort);
119 Optional<NormalizedNode> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get();
120 assertEquals("After commit read: isPresent", true, afterCommitRead.isPresent());
121 assertEquals("After commit read: data", testNode, afterCommitRead.get());
125 public void testDelete() throws Exception {
127 DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
128 assertNotNull(writeTx);
130 // Write /test and commit
132 writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
134 assertThreePhaseCommit(writeTx.ready());
136 Optional<NormalizedNode> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get();
137 assertEquals("After commit read: isPresent", true, afterCommitRead.isPresent());
139 // Delete /test and verify
141 writeTx = domStore.newWriteOnlyTransaction();
143 writeTx.delete(TestModel.TEST_PATH);
145 assertThreePhaseCommit(writeTx.ready());
147 afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get();
148 assertEquals("After commit read: isPresent", false, afterCommitRead.isPresent());
152 public void testMerge() throws Exception {
154 DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
155 assertNotNull(writeTx);
157 ContainerNode containerNode = ImmutableContainerNodeBuilder.create()
158 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
159 .addChild(ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
160 .addChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME,
161 TestModel.ID_QNAME, 1)).build()).build();
163 writeTx.merge(TestModel.TEST_PATH, containerNode);
165 assertThreePhaseCommit(writeTx.ready());
167 Optional<NormalizedNode> afterCommitRead =
168 domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get();
169 assertEquals("After commit read: isPresent", true, afterCommitRead.isPresent());
170 assertEquals("After commit read: data", containerNode, afterCommitRead.get());
172 // Merge a new list entry node
174 writeTx = domStore.newWriteOnlyTransaction();
175 assertNotNull(writeTx);
177 containerNode = ImmutableContainerNodeBuilder.create()
178 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
179 .addChild(ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
180 .addChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME,
181 TestModel.ID_QNAME, 1))
182 .addChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME,
183 TestModel.ID_QNAME, 2)).build()).build();
185 writeTx.merge(TestModel.TEST_PATH, containerNode);
187 assertThreePhaseCommit(writeTx.ready());
189 afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get();
190 assertEquals("After commit read: isPresent", true, afterCommitRead.isPresent());
191 assertEquals("After commit read: data", containerNode, afterCommitRead.get());
196 public void testExistsForExistingData() throws Exception {
198 DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
199 assertNotNull(writeTx);
201 ContainerNode containerNode = ImmutableContainerNodeBuilder.create()
202 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
203 .addChild(ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
204 .addChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME,
205 TestModel.ID_QNAME, 1)).build()).build();
207 writeTx.merge(TestModel.TEST_PATH, containerNode);
209 ListenableFuture<Boolean> exists = writeTx.exists(TestModel.TEST_PATH);
211 assertEquals(Boolean.TRUE, exists.get());
213 DOMStoreThreePhaseCommitCohort ready = writeTx.ready();
215 ready.preCommit().get();
217 ready.commit().get();
219 DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
220 assertNotNull(readTx);
223 readTx.exists(TestModel.TEST_PATH);
225 assertEquals(Boolean.TRUE, exists.get());
229 public void testExistsForNonExistingData() throws Exception {
231 DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
232 assertNotNull(writeTx);
234 ListenableFuture<Boolean> exists = writeTx.exists(TestModel.TEST_PATH);
236 assertEquals(Boolean.FALSE, exists.get());
238 DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
239 assertNotNull(readTx);
242 readTx.exists(TestModel.TEST_PATH);
244 assertEquals(Boolean.FALSE, exists.get());
247 @Test(expected = ReadFailedException.class)
248 @SuppressWarnings({"checkstyle:IllegalThrows", "checkstyle:AvoidHidingCauseException"})
249 public void testExistsThrowsReadFailedException() throws Throwable {
251 DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
252 assertNotNull(readTx);
257 readTx.exists(TestModel.TEST_PATH).get();
258 } catch (ExecutionException e) {
264 @SuppressWarnings("checkstyle:IllegalThrows")
265 @Test(expected = ReadFailedException.class)
266 public void testReadWithReadOnlyTransactionClosed() throws Throwable {
268 DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
269 assertNotNull(readTx);
273 doReadAndThrowEx(readTx);
276 @SuppressWarnings("checkstyle:IllegalThrows")
277 @Test(expected = ReadFailedException.class)
278 public void testReadWithReadOnlyTransactionFailure() throws Throwable {
280 DataTreeSnapshot mockSnapshot = Mockito.mock(DataTreeSnapshot.class);
281 Mockito.doThrow(new RuntimeException("mock ex")).when(mockSnapshot)
282 .readNode(Mockito.any(YangInstanceIdentifier.class));
284 DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadTransaction("1", true, mockSnapshot);
286 doReadAndThrowEx(readTx);
289 @SuppressWarnings("checkstyle:IllegalThrows")
290 @Test(expected = ReadFailedException.class)
291 public void testReadWithReadWriteTransactionClosed() throws Throwable {
293 DOMStoreReadTransaction readTx = domStore.newReadWriteTransaction();
294 assertNotNull(readTx);
298 doReadAndThrowEx(readTx);
301 @SuppressWarnings("checkstyle:IllegalThrows")
302 @Test(expected = ReadFailedException.class)
303 public void testReadWithReadWriteTransactionFailure() throws Throwable {
305 DataTreeSnapshot mockSnapshot = Mockito.mock(DataTreeSnapshot.class);
306 DataTreeModification mockModification = Mockito.mock(DataTreeModification.class);
307 Mockito.doThrow(new RuntimeException("mock ex")).when(mockModification)
308 .readNode(Mockito.any(YangInstanceIdentifier.class));
309 Mockito.doReturn(mockModification).when(mockSnapshot).newModification();
310 @SuppressWarnings("unchecked")
311 TransactionReadyPrototype<String> mockReady = Mockito.mock(TransactionReadyPrototype.class);
312 DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadWriteTransaction(
313 "1", false, mockSnapshot, mockReady);
315 doReadAndThrowEx(readTx);
318 @SuppressWarnings({ "checkstyle:IllegalThrows", "checkstyle:avoidHidingCauseException" })
319 private static void doReadAndThrowEx(final DOMStoreReadTransaction readTx) throws Throwable {
321 readTx.read(TestModel.TEST_PATH).get();
322 } catch (ExecutionException e) {
327 @Test(expected = IllegalStateException.class)
328 public void testWriteWithTransactionReady() throws Exception {
330 DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
335 writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
338 @Test(expected = IllegalStateException.class)
339 public void testReadyWithTransactionAlreadyReady() throws Exception {
341 DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
350 public void testReadyWithMissingMandatoryData() throws InterruptedException {
351 DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
352 NormalizedNode testNode = ImmutableContainerNodeBuilder.create()
353 .withNodeIdentifier(new NodeIdentifier(TestModel.MANDATORY_DATA_TEST_QNAME))
354 .addChild(ImmutableNodes.leafNode(TestModel.OPTIONAL_QNAME, "data"))
356 writeTx.write(TestModel.MANDATORY_DATA_TEST_PATH, testNode);
357 DOMStoreThreePhaseCommitCohort ready = writeTx.ready();
359 ready.canCommit().get();
360 Assert.fail("Expected exception on canCommit");
361 } catch (ExecutionException e) {
367 public void testTransactionAbort() throws InterruptedException, ExecutionException {
369 DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
370 assertNotNull(writeTx);
372 assertTestContainerWrite(writeTx);
374 DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
376 assertTrue(cohort.canCommit().get());
377 cohort.preCommit().get();
378 cohort.abort().get();
380 assertEquals(Optional.empty(), domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get());
384 public void testTransactionChain() throws InterruptedException, ExecutionException {
385 DOMStoreTransactionChain txChain = domStore.createTransactionChain();
386 assertNotNull(txChain);
389 * We alocate new read-write transaction and write /test.
391 DOMStoreReadWriteTransaction firstTx = txChain.newReadWriteTransaction();
392 assertTestContainerWrite(firstTx);
395 * First transaction is marked as ready, we are able to allocate chained
398 final DOMStoreThreePhaseCommitCohort firstWriteTxCohort = firstTx.ready();
401 * We alocate chained transaction - read transaction, note first one is
402 * still not commited to datastore.
404 DOMStoreReadTransaction secondReadTx = txChain.newReadOnlyTransaction();
407 * We test if we are able to read data from tx, read should not fail
408 * since we are using chained transaction.
410 assertTestContainerExists(secondReadTx);
413 * We alocate next transaction, which is still based on first one, but
416 DOMStoreReadWriteTransaction thirdDeleteTx = txChain.newReadWriteTransaction();
419 * We test existence of /test in third transaction container should
420 * still be visible from first one (which is still uncommmited).
422 assertTestContainerExists(thirdDeleteTx);
425 * We delete node in third transaction.
427 thirdDeleteTx.delete(TestModel.TEST_PATH);
430 * third transaction is sealed.
432 DOMStoreThreePhaseCommitCohort thirdDeleteTxCohort = thirdDeleteTx.ready();
435 * We commit first transaction.
438 assertThreePhaseCommit(firstWriteTxCohort);
440 // Alocates store transacion
441 DOMStoreReadTransaction storeReadTx = domStore.newReadOnlyTransaction();
443 * We verify transaction is commited to store, container should exists
446 assertTestContainerExists(storeReadTx);
448 * We commit third transaction
451 assertThreePhaseCommit(thirdDeleteTxCohort);
456 public void testTransactionConflict() throws InterruptedException, ExecutionException {
457 DOMStoreReadWriteTransaction txOne = domStore.newReadWriteTransaction();
458 DOMStoreReadWriteTransaction txTwo = domStore.newReadWriteTransaction();
459 assertTestContainerWrite(txOne);
460 assertTestContainerWrite(txTwo);
463 * Commits transaction
465 assertThreePhaseCommit(txOne.ready());
468 * Asserts that txTwo could not be commited
470 assertFalse(txTwo.ready().canCommit().get());
473 private static void assertThreePhaseCommit(final DOMStoreThreePhaseCommitCohort cohort)
474 throws InterruptedException, ExecutionException {
475 assertTrue(cohort.canCommit().get());
476 cohort.preCommit().get();
477 cohort.commit().get();
480 private static Optional<NormalizedNode> assertTestContainerWrite(final DOMStoreReadWriteTransaction writeTx)
481 throws InterruptedException, ExecutionException {
484 * Writes /test in writeTx
487 writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
489 return assertTestContainerExists(writeTx);
493 * Reads /test from readTx Read should return container.
495 private static Optional<NormalizedNode> assertTestContainerExists(final DOMStoreReadTransaction readTx)
496 throws InterruptedException, ExecutionException {
498 ListenableFuture<Optional<NormalizedNode>> writeTxContainer = readTx.read(TestModel.TEST_PATH);
499 assertTrue(writeTxContainer.get().isPresent());
500 return writeTxContainer.get();