Adopt odlparent-10.0.0/yangtools-8.0.0-SNAPSHOT
[mdsal.git] / dom / mdsal-dom-inmemory-datastore / src / test / java / org / opendaylight / mdsal / dom / store / inmemory / 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.mdsal.dom.store.inmemory;
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.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.impl.schema.ImmutableNodes;
39 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
40 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeModification;
41 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeSnapshot;
42 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
43
44 public class InMemoryDataStoreTest {
45     private static EffectiveModelContext SCHEMA_CONTEXT;
46
47     private InMemoryDOMDataStore domStore;
48
49     @BeforeClass
50     public static void beforeClass() {
51         SCHEMA_CONTEXT = TestModel.createTestContext();
52     }
53
54     @AfterClass
55     public static void afterClass() {
56         SCHEMA_CONTEXT = null;
57     }
58
59     @Before
60     public void setupStore() {
61         domStore = new InMemoryDOMDataStore("TEST", MoreExecutors.newDirectExecutorService());
62         domStore.onModelContextUpdated(SCHEMA_CONTEXT);
63     }
64
65     @Test
66     public void testTransactionIsolation() throws InterruptedException, ExecutionException {
67
68         assertNotNull(domStore);
69
70         DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
71         assertNotNull(readTx);
72
73         DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
74         assertNotNull(writeTx);
75
76         /**
77          * Writes /test in writeTx.
78          */
79         NormalizedNode testNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
80         writeTx.write(TestModel.TEST_PATH, testNode);
81
82         /**
83          * Reads /test from writeTx Read should return container.
84          */
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());
88
89         /**
90          * Reads /test from readTx Read should return Absent.
91          */
92         ListenableFuture<Optional<NormalizedNode>> readTxContainer = readTx.read(TestModel.TEST_PATH);
93         assertEquals("read: isPresent", false, readTxContainer.get().isPresent());
94     }
95
96     @Test
97     public void testTransactionCommit() throws InterruptedException, ExecutionException {
98
99         DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
100         assertNotNull(writeTx);
101
102         /**
103          * Writes /test in writeTx.
104          */
105         NormalizedNode testNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
106         writeTx.write(TestModel.TEST_PATH, testNode);
107
108         /**
109          * Reads /test from writeTx Read should return container.
110          */
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());
114
115         DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
116
117         assertThreePhaseCommit(cohort);
118
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());
122     }
123
124     @Test
125     public void testDelete() throws Exception {
126
127         DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
128         assertNotNull(writeTx);
129
130         // Write /test and commit
131
132         writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
133
134         assertThreePhaseCommit(writeTx.ready());
135
136         Optional<NormalizedNode> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get();
137         assertEquals("After commit read: isPresent", true, afterCommitRead.isPresent());
138
139         // Delete /test and verify
140
141         writeTx = domStore.newWriteOnlyTransaction();
142
143         writeTx.delete(TestModel.TEST_PATH);
144
145         assertThreePhaseCommit(writeTx.ready());
146
147         afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get();
148         assertEquals("After commit read: isPresent", false, afterCommitRead.isPresent());
149     }
150
151     @Test
152     public void testMerge() throws Exception {
153
154         DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
155         assertNotNull(writeTx);
156
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();
162
163         writeTx.merge(TestModel.TEST_PATH, containerNode);
164
165         assertThreePhaseCommit(writeTx.ready());
166
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());
171
172         // Merge a new list entry node
173
174         writeTx = domStore.newWriteOnlyTransaction();
175         assertNotNull(writeTx);
176
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();
184
185         writeTx.merge(TestModel.TEST_PATH, containerNode);
186
187         assertThreePhaseCommit(writeTx.ready());
188
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());
192     }
193
194
195     @Test
196     public void testExistsForExistingData() throws Exception {
197
198         DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
199         assertNotNull(writeTx);
200
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();
206
207         writeTx.merge(TestModel.TEST_PATH, containerNode);
208
209         ListenableFuture<Boolean> exists = writeTx.exists(TestModel.TEST_PATH);
210
211         assertEquals(Boolean.TRUE, exists.get());
212
213         DOMStoreThreePhaseCommitCohort ready = writeTx.ready();
214
215         ready.preCommit().get();
216
217         ready.commit().get();
218
219         DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
220         assertNotNull(readTx);
221
222         exists =
223             readTx.exists(TestModel.TEST_PATH);
224
225         assertEquals(Boolean.TRUE, exists.get());
226     }
227
228     @Test
229     public void testExistsForNonExistingData() throws Exception {
230
231         DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
232         assertNotNull(writeTx);
233
234         ListenableFuture<Boolean> exists = writeTx.exists(TestModel.TEST_PATH);
235
236         assertEquals(Boolean.FALSE, exists.get());
237
238         DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
239         assertNotNull(readTx);
240
241         exists =
242             readTx.exists(TestModel.TEST_PATH);
243
244         assertEquals(Boolean.FALSE, exists.get());
245     }
246
247     @Test(expected = ReadFailedException.class)
248     @SuppressWarnings({"checkstyle:IllegalThrows", "checkstyle:AvoidHidingCauseException"})
249     public void testExistsThrowsReadFailedException() throws Throwable {
250
251         DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
252         assertNotNull(readTx);
253
254         readTx.close();
255
256         try {
257             readTx.exists(TestModel.TEST_PATH).get();
258         } catch (ExecutionException e) {
259             throw e.getCause();
260         }
261     }
262
263
264     @SuppressWarnings("checkstyle:IllegalThrows")
265     @Test(expected = ReadFailedException.class)
266     public void testReadWithReadOnlyTransactionClosed() throws Throwable {
267
268         DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
269         assertNotNull(readTx);
270
271         readTx.close();
272
273         doReadAndThrowEx(readTx);
274     }
275
276     @SuppressWarnings("checkstyle:IllegalThrows")
277     @Test(expected = ReadFailedException.class)
278     public void testReadWithReadOnlyTransactionFailure() throws Throwable {
279
280         DataTreeSnapshot mockSnapshot = Mockito.mock(DataTreeSnapshot.class);
281         Mockito.doThrow(new RuntimeException("mock ex")).when(mockSnapshot)
282         .readNode(Mockito.any(YangInstanceIdentifier.class));
283
284         DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadTransaction("1", true, mockSnapshot);
285
286         doReadAndThrowEx(readTx);
287     }
288
289     @SuppressWarnings("checkstyle:IllegalThrows")
290     @Test(expected = ReadFailedException.class)
291     public void testReadWithReadWriteTransactionClosed() throws Throwable {
292
293         DOMStoreReadTransaction readTx = domStore.newReadWriteTransaction();
294         assertNotNull(readTx);
295
296         readTx.close();
297
298         doReadAndThrowEx(readTx);
299     }
300
301     @SuppressWarnings("checkstyle:IllegalThrows")
302     @Test(expected = ReadFailedException.class)
303     public void testReadWithReadWriteTransactionFailure() throws Throwable {
304
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);
314
315         doReadAndThrowEx(readTx);
316     }
317
318     @SuppressWarnings({ "checkstyle:IllegalThrows", "checkstyle:avoidHidingCauseException" })
319     private static void doReadAndThrowEx(final DOMStoreReadTransaction readTx) throws Throwable {
320         try {
321             readTx.read(TestModel.TEST_PATH).get();
322         } catch (ExecutionException e) {
323             throw e.getCause();
324         }
325     }
326
327     @Test(expected = IllegalStateException.class)
328     public void testWriteWithTransactionReady() throws Exception {
329
330         DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
331
332         writeTx.ready();
333
334         // Should throw ex
335         writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
336     }
337
338     @Test(expected = IllegalStateException.class)
339     public void testReadyWithTransactionAlreadyReady() throws Exception {
340
341         DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
342
343         writeTx.ready();
344
345         // Should throw ex
346         writeTx.ready();
347     }
348
349     @Test
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"))
355                 .build();
356         writeTx.write(TestModel.MANDATORY_DATA_TEST_PATH, testNode);
357         DOMStoreThreePhaseCommitCohort ready = writeTx.ready();
358         try {
359             ready.canCommit().get();
360             Assert.fail("Expected exception on canCommit");
361         } catch (ExecutionException e) {
362             // nop
363         }
364     }
365
366     @Test
367     public void testTransactionAbort() throws InterruptedException, ExecutionException {
368
369         DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
370         assertNotNull(writeTx);
371
372         assertTestContainerWrite(writeTx);
373
374         DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
375
376         assertTrue(cohort.canCommit().get());
377         cohort.preCommit().get();
378         cohort.abort().get();
379
380         assertEquals(Optional.empty(), domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get());
381     }
382
383     @Test
384     public void testTransactionChain() throws InterruptedException, ExecutionException {
385         DOMStoreTransactionChain txChain = domStore.createTransactionChain();
386         assertNotNull(txChain);
387
388         /**
389          * We alocate new read-write transaction and write /test.
390          */
391         DOMStoreReadWriteTransaction firstTx = txChain.newReadWriteTransaction();
392         assertTestContainerWrite(firstTx);
393
394         /**
395          * First transaction is marked as ready, we are able to allocate chained
396          * transactions.
397          */
398         final DOMStoreThreePhaseCommitCohort firstWriteTxCohort = firstTx.ready();
399
400         /**
401          * We alocate chained transaction - read transaction, note first one is
402          * still not commited to datastore.
403          */
404         DOMStoreReadTransaction secondReadTx = txChain.newReadOnlyTransaction();
405
406         /**
407          * We test if we are able to read data from tx, read should not fail
408          * since we are using chained transaction.
409          */
410         assertTestContainerExists(secondReadTx);
411
412         /**
413          * We alocate next transaction, which is still based on first one, but
414          * is read-write.
415          */
416         DOMStoreReadWriteTransaction thirdDeleteTx = txChain.newReadWriteTransaction();
417
418         /**
419          * We test existence of /test in third transaction container should
420          * still be visible from first one (which is still uncommmited).
421          */
422         assertTestContainerExists(thirdDeleteTx);
423
424         /**
425          * We delete node in third transaction.
426          */
427         thirdDeleteTx.delete(TestModel.TEST_PATH);
428
429         /**
430          * third transaction is sealed.
431          */
432         DOMStoreThreePhaseCommitCohort thirdDeleteTxCohort = thirdDeleteTx.ready();
433
434         /**
435          * We commit first transaction.
436          *
437          */
438         assertThreePhaseCommit(firstWriteTxCohort);
439
440         // Alocates store transacion
441         DOMStoreReadTransaction storeReadTx = domStore.newReadOnlyTransaction();
442         /**
443          * We verify transaction is commited to store, container should exists
444          * in datastore.
445          */
446         assertTestContainerExists(storeReadTx);
447         /**
448          * We commit third transaction
449          *
450          */
451         assertThreePhaseCommit(thirdDeleteTxCohort);
452     }
453
454     @Test
455     @Ignore
456     public void testTransactionConflict() throws InterruptedException, ExecutionException {
457         DOMStoreReadWriteTransaction txOne = domStore.newReadWriteTransaction();
458         DOMStoreReadWriteTransaction txTwo = domStore.newReadWriteTransaction();
459         assertTestContainerWrite(txOne);
460         assertTestContainerWrite(txTwo);
461
462         /**
463          * Commits transaction
464          */
465         assertThreePhaseCommit(txOne.ready());
466
467         /**
468          * Asserts that txTwo could not be commited
469          */
470         assertFalse(txTwo.ready().canCommit().get());
471     }
472
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();
478     }
479
480     private static Optional<NormalizedNode> assertTestContainerWrite(final DOMStoreReadWriteTransaction writeTx)
481             throws InterruptedException, ExecutionException {
482         /**
483          *
484          * Writes /test in writeTx
485          *
486          */
487         writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
488
489         return assertTestContainerExists(writeTx);
490     }
491
492     /**
493      * Reads /test from readTx Read should return container.
494      */
495     private static Optional<NormalizedNode> assertTestContainerExists(final DOMStoreReadTransaction readTx)
496             throws InterruptedException, ExecutionException {
497
498         ListenableFuture<Optional<NormalizedNode>> writeTxContainer = readTx.read(TestModel.TEST_PATH);
499         assertTrue(writeTxContainer.get().isPresent());
500         return writeTxContainer.get();
501     }
502 }