2476d22f63488be33321320387c5089527ec07c3
[mdsal.git] / dom / mdsal-dom-inmemory-datastore / src / test / java / org / opendaylight / controller / md / sal / dom / store / impl / InMemoryDataStoreTest.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.controller.md.sal.dom.store.impl;
9
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;
14
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.Before;
20 import org.junit.Ignore;
21 import org.junit.Test;
22 import org.mockito.Mockito;
23 import org.opendaylight.mdsal.common.api.ReadFailedException;
24 import org.opendaylight.mdsal.dom.spi.store.DOMStoreReadTransaction;
25 import org.opendaylight.mdsal.dom.spi.store.DOMStoreReadWriteTransaction;
26 import org.opendaylight.mdsal.dom.spi.store.DOMStoreThreePhaseCommitCohort;
27 import org.opendaylight.mdsal.dom.spi.store.DOMStoreTransactionChain;
28 import org.opendaylight.mdsal.dom.spi.store.DOMStoreWriteTransaction;
29 import org.opendaylight.mdsal.dom.spi.store.SnapshotBackedTransactions;
30 import org.opendaylight.mdsal.dom.spi.store.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
31 import org.opendaylight.mdsal.dom.store.inmemory.InMemoryDOMDataStore;
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;
41
42 public class InMemoryDataStoreTest {
43
44     private SchemaContext schemaContext;
45     private InMemoryDOMDataStore domStore;
46
47     @Before
48     public void setupStore() {
49         domStore = new InMemoryDOMDataStore("TEST", MoreExecutors.newDirectExecutorService());
50         schemaContext = TestModel.createTestContext();
51         domStore.onGlobalContextUpdated(schemaContext);
52     }
53
54     @Test
55     public void testTransactionIsolation() throws InterruptedException, ExecutionException {
56
57         assertNotNull(domStore);
58
59         DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
60         assertNotNull(readTx);
61
62         DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
63         assertNotNull(writeTx);
64
65         /**
66          * Writes /test in writeTx.
67          */
68         NormalizedNode<?, ?> testNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
69         writeTx.write(TestModel.TEST_PATH, testNode);
70
71         /**
72          * Reads /test from writeTx Read should return container.
73          */
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());
77
78         /**
79          * Reads /test from readTx Read should return Absent.
80          */
81         ListenableFuture<Optional<NormalizedNode<?, ?>>> readTxContainer = readTx.read(TestModel.TEST_PATH);
82         assertEquals("read: isPresent", false, readTxContainer.get().isPresent());
83     }
84
85     @Test
86     public void testTransactionCommit() throws InterruptedException, ExecutionException {
87
88         DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
89         assertNotNull(writeTx);
90
91         /**
92          * Writes /test in writeTx.
93          */
94         NormalizedNode<?, ?> testNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
95         writeTx.write(TestModel.TEST_PATH, testNode);
96
97         /**
98          * Reads /test from writeTx Read should return container.
99          */
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());
103
104         DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
105
106         assertThreePhaseCommit(cohort);
107
108         Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)
109                 .get();
110         assertEquals("After commit read: isPresent", true, afterCommitRead.isPresent());
111         assertEquals("After commit read: data", testNode, afterCommitRead.get());
112     }
113
114     @Test
115     public void testDelete() throws Exception {
116
117         DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
118         assertNotNull(writeTx);
119
120         // Write /test and commit
121
122         writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
123
124         assertThreePhaseCommit(writeTx.ready());
125
126         Optional<NormalizedNode<?, ?>> afterCommitRead =
127                 domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get();
128         assertEquals("After commit read: isPresent", true, afterCommitRead.isPresent());
129
130         // Delete /test and verify
131
132         writeTx = domStore.newWriteOnlyTransaction();
133
134         writeTx.delete(TestModel.TEST_PATH);
135
136         assertThreePhaseCommit(writeTx.ready());
137
138         afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get();
139         assertEquals("After commit read: isPresent", false, afterCommitRead.isPresent());
140     }
141
142     @Test
143     public void testMerge() throws Exception {
144
145         DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
146         assertNotNull(writeTx);
147
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();
153
154         writeTx.merge(TestModel.TEST_PATH, containerNode);
155
156         assertThreePhaseCommit(writeTx.ready());
157
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());
162
163         // Merge a new list entry node
164
165         writeTx = domStore.newWriteOnlyTransaction();
166         assertNotNull(writeTx);
167
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();
175
176         writeTx.merge(TestModel.TEST_PATH, containerNode);
177
178         assertThreePhaseCommit(writeTx.ready());
179
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());
183     }
184
185
186     @Test
187     public void testExistsForExistingData() throws Exception {
188
189         DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
190         assertNotNull(writeTx);
191
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();
197
198         writeTx.merge(TestModel.TEST_PATH, containerNode);
199
200         ListenableFuture<Boolean> exists = writeTx.exists(TestModel.TEST_PATH);
201
202         assertEquals(Boolean.TRUE, exists.get());
203
204         DOMStoreThreePhaseCommitCohort ready = writeTx.ready();
205
206         ready.preCommit().get();
207
208         ready.commit().get();
209
210         DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
211         assertNotNull(readTx);
212
213         exists =
214             readTx.exists(TestModel.TEST_PATH);
215
216         assertEquals(Boolean.TRUE, exists.get());
217     }
218
219     @Test
220     public void testExistsForNonExistingData() throws Exception {
221
222         DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
223         assertNotNull(writeTx);
224
225         ListenableFuture<Boolean> exists = writeTx.exists(TestModel.TEST_PATH);
226
227         assertEquals(Boolean.FALSE, exists.get());
228
229         DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
230         assertNotNull(readTx);
231
232         exists =
233             readTx.exists(TestModel.TEST_PATH);
234
235         assertEquals(Boolean.FALSE, exists.get());
236     }
237
238     @Test(expected = ReadFailedException.class)
239     public void testExistsThrowsReadFailedException() throws Exception {
240
241         DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
242         assertNotNull(readTx);
243
244         readTx.close();
245
246         readTx.exists(TestModel.TEST_PATH).checkedGet();
247     }
248
249
250     @SuppressWarnings("checkstyle:IllegalThrows")
251     @Test(expected = ReadFailedException.class)
252     public void testReadWithReadOnlyTransactionClosed() throws Throwable {
253
254         DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
255         assertNotNull(readTx);
256
257         readTx.close();
258
259         doReadAndThrowEx(readTx);
260     }
261
262     @SuppressWarnings("checkstyle:IllegalThrows")
263     @Test(expected = ReadFailedException.class)
264     public void testReadWithReadOnlyTransactionFailure() throws Throwable {
265
266         DataTreeSnapshot mockSnapshot = Mockito.mock(DataTreeSnapshot.class);
267         Mockito.doThrow(new RuntimeException("mock ex")).when(mockSnapshot)
268         .readNode(Mockito.any(YangInstanceIdentifier.class));
269
270         DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadTransaction("1", true, mockSnapshot);
271
272         doReadAndThrowEx(readTx);
273     }
274
275     @SuppressWarnings("checkstyle:IllegalThrows")
276     @Test(expected = ReadFailedException.class)
277     public void testReadWithReadWriteTransactionClosed() throws Throwable {
278
279         DOMStoreReadTransaction readTx = domStore.newReadWriteTransaction();
280         assertNotNull(readTx);
281
282         readTx.close();
283
284         doReadAndThrowEx(readTx);
285     }
286
287     @SuppressWarnings("checkstyle:IllegalThrows")
288     @Test(expected = ReadFailedException.class)
289     public void testReadWithReadWriteTransactionFailure() throws Throwable {
290
291         DataTreeSnapshot mockSnapshot = Mockito.mock(DataTreeSnapshot.class);
292         DataTreeModification mockModification = Mockito.mock(DataTreeModification.class);
293         Mockito.doThrow(new RuntimeException("mock ex")).when(mockModification)
294         .readNode(Mockito.any(YangInstanceIdentifier.class));
295         Mockito.doReturn(mockModification).when(mockSnapshot).newModification();
296         @SuppressWarnings("unchecked")
297         TransactionReadyPrototype<String> mockReady = Mockito.mock(TransactionReadyPrototype.class);
298         DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadWriteTransaction(
299                 "1", false, mockSnapshot, mockReady);
300
301         doReadAndThrowEx(readTx);
302     }
303
304     @SuppressWarnings({ "checkstyle:IllegalThrows", "checkstyle:avoidHidingCauseException" })
305     private static void doReadAndThrowEx(final DOMStoreReadTransaction readTx) throws Throwable {
306         try {
307             readTx.read(TestModel.TEST_PATH).get();
308         } catch (ExecutionException e) {
309             throw e.getCause();
310         }
311     }
312
313     @Test(expected = IllegalStateException.class)
314     public void testWriteWithTransactionReady() throws Exception {
315
316         DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
317
318         writeTx.ready();
319
320         // Should throw ex
321         writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
322     }
323
324     @Test(expected = IllegalStateException.class)
325     public void testReadyWithTransactionAlreadyReady() throws Exception {
326
327         DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
328
329         writeTx.ready();
330
331         // Should throw ex
332         writeTx.ready();
333     }
334
335     @Test
336     public void testTransactionAbort() throws InterruptedException, ExecutionException {
337
338         DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
339         assertNotNull(writeTx);
340
341         assertTestContainerWrite(writeTx);
342
343         DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
344
345         assertTrue(cohort.canCommit().get().booleanValue());
346         cohort.preCommit().get();
347         cohort.abort().get();
348
349         Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)
350                 .get();
351         assertFalse(afterCommitRead.isPresent());
352     }
353
354     @Test
355     public void testTransactionChain() throws InterruptedException, ExecutionException {
356         DOMStoreTransactionChain txChain = domStore.createTransactionChain();
357         assertNotNull(txChain);
358
359         /**
360          * We alocate new read-write transaction and write /test.
361          */
362         DOMStoreReadWriteTransaction firstTx = txChain.newReadWriteTransaction();
363         assertTestContainerWrite(firstTx);
364
365         /**
366          * First transaction is marked as ready, we are able to allocate chained
367          * transactions.
368          */
369         final DOMStoreThreePhaseCommitCohort firstWriteTxCohort = firstTx.ready();
370
371         /**
372          * We alocate chained transaction - read transaction, note first one is
373          * still not commited to datastore.
374          */
375         DOMStoreReadTransaction secondReadTx = txChain.newReadOnlyTransaction();
376
377         /**
378          * We test if we are able to read data from tx, read should not fail
379          * since we are using chained transaction.
380          */
381         assertTestContainerExists(secondReadTx);
382
383         /**
384          * We alocate next transaction, which is still based on first one, but
385          * is read-write.
386          */
387         DOMStoreReadWriteTransaction thirdDeleteTx = txChain.newReadWriteTransaction();
388
389         /**
390          * We test existence of /test in third transaction container should
391          * still be visible from first one (which is still uncommmited).
392          */
393         assertTestContainerExists(thirdDeleteTx);
394
395         /**
396          * We delete node in third transaction.
397          */
398         thirdDeleteTx.delete(TestModel.TEST_PATH);
399
400         /**
401          * third transaction is sealed.
402          */
403         DOMStoreThreePhaseCommitCohort thirdDeleteTxCohort = thirdDeleteTx.ready();
404
405         /**
406          * We commit first transaction.
407          *
408          */
409         assertThreePhaseCommit(firstWriteTxCohort);
410
411         // Alocates store transacion
412         DOMStoreReadTransaction storeReadTx = domStore.newReadOnlyTransaction();
413         /**
414          * We verify transaction is commited to store, container should exists
415          * in datastore.
416          */
417         assertTestContainerExists(storeReadTx);
418         /**
419          * We commit third transaction
420          *
421          */
422         assertThreePhaseCommit(thirdDeleteTxCohort);
423     }
424
425     @Test
426     @Ignore
427     public void testTransactionConflict() throws InterruptedException, ExecutionException {
428         DOMStoreReadWriteTransaction txOne = domStore.newReadWriteTransaction();
429         DOMStoreReadWriteTransaction txTwo = domStore.newReadWriteTransaction();
430         assertTestContainerWrite(txOne);
431         assertTestContainerWrite(txTwo);
432
433         /**
434          * Commits transaction
435          */
436         assertThreePhaseCommit(txOne.ready());
437
438         /**
439          * Asserts that txTwo could not be commited
440          */
441         assertFalse(txTwo.ready().canCommit().get());
442     }
443
444     private static void assertThreePhaseCommit(final DOMStoreThreePhaseCommitCohort cohort)
445             throws InterruptedException, ExecutionException {
446         assertTrue(cohort.canCommit().get().booleanValue());
447         cohort.preCommit().get();
448         cohort.commit().get();
449     }
450
451     private static Optional<NormalizedNode<?, ?>> assertTestContainerWrite(final DOMStoreReadWriteTransaction writeTx)
452             throws InterruptedException, ExecutionException {
453         /**
454          *
455          * Writes /test in writeTx
456          *
457          */
458         writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
459
460         return assertTestContainerExists(writeTx);
461     }
462
463     /**
464      * Reads /test from readTx Read should return container.
465      */
466     private static Optional<NormalizedNode<?, ?>> assertTestContainerExists(final DOMStoreReadTransaction readTx)
467             throws InterruptedException, ExecutionException {
468
469         ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = readTx.read(TestModel.TEST_PATH);
470         assertTrue(writeTxContainer.get().isPresent());
471         return writeTxContainer.get();
472     }
473
474 }