Do not use Builders in InMemoryDataStoreTest
[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.hamcrest.CoreMatchers.containsString;
11 import static org.hamcrest.CoreMatchers.instanceOf;
12 import static org.hamcrest.MatcherAssert.assertThat;
13 import static org.junit.Assert.assertEquals;
14 import static org.junit.Assert.assertFalse;
15 import static org.junit.Assert.assertNotNull;
16 import static org.junit.Assert.assertThrows;
17 import static org.junit.Assert.assertTrue;
18 import static org.mockito.ArgumentMatchers.any;
19 import static org.mockito.Mockito.doReturn;
20 import static org.mockito.Mockito.doThrow;
21 import static org.mockito.Mockito.mock;
22
23 import com.google.common.util.concurrent.Futures;
24 import com.google.common.util.concurrent.ListenableFuture;
25 import com.google.common.util.concurrent.MoreExecutors;
26 import java.util.Optional;
27 import java.util.concurrent.ExecutionException;
28 import org.junit.AfterClass;
29 import org.junit.Before;
30 import org.junit.BeforeClass;
31 import org.junit.Ignore;
32 import org.junit.Test;
33 import org.opendaylight.mdsal.common.api.ReadFailedException;
34 import org.opendaylight.mdsal.dom.spi.store.DOMStoreReadTransaction;
35 import org.opendaylight.mdsal.dom.spi.store.DOMStoreReadWriteTransaction;
36 import org.opendaylight.mdsal.dom.spi.store.DOMStoreThreePhaseCommitCohort;
37 import org.opendaylight.mdsal.dom.spi.store.DOMStoreTransactionChain;
38 import org.opendaylight.mdsal.dom.spi.store.DOMStoreWriteTransaction;
39 import org.opendaylight.mdsal.dom.spi.store.SnapshotBackedTransactions;
40 import org.opendaylight.mdsal.dom.spi.store.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
41 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
42 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
43 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
44 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
45 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
46 import org.opendaylight.yangtools.yang.data.spi.node.ImmutableNodes;
47 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeModification;
48 import org.opendaylight.yangtools.yang.data.tree.api.DataTreeSnapshot;
49 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
50
51 public class InMemoryDataStoreTest {
52     private static EffectiveModelContext SCHEMA_CONTEXT;
53
54     private InMemoryDOMDataStore domStore;
55
56     @BeforeClass
57     public static void beforeClass() {
58         SCHEMA_CONTEXT = TestModel.createTestContext();
59     }
60
61     @AfterClass
62     public static void afterClass() {
63         SCHEMA_CONTEXT = null;
64     }
65
66     @Before
67     public void setupStore() {
68         domStore = new InMemoryDOMDataStore("TEST", MoreExecutors.newDirectExecutorService());
69         domStore.onModelContextUpdated(SCHEMA_CONTEXT);
70     }
71
72     @Test
73     public void testTransactionIsolation() throws Exception {
74         DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
75         assertNotNull(readTx);
76
77         DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
78         assertNotNull(writeTx);
79
80         /**
81          * Writes /test in writeTx.
82          */
83         final var testNode = testContainer();
84         writeTx.write(TestModel.TEST_PATH, testNode);
85
86         /**
87          * Reads /test from writeTx Read should return container.
88          */
89         assertEquals(Optional.of(testNode), Futures.getDone(writeTx.read(TestModel.TEST_PATH)));
90
91         /**
92          * Reads /test from readTx Read should return Absent.
93          */
94         assertEquals(Optional.empty(), Futures.getDone(readTx.read(TestModel.TEST_PATH)));
95     }
96
97     private static ContainerNode testContainer() {
98         return ImmutableNodes.newContainerBuilder()
99             .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
100             .build();
101     }
102
103     @Test
104     public void testTransactionCommit() throws Exception {
105
106         DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
107         assertNotNull(writeTx);
108
109         /**
110          * Writes /test in writeTx.
111          */
112         var testNode = testContainer();
113         writeTx.write(TestModel.TEST_PATH, testNode);
114
115         /**
116          * Reads /test from writeTx Read should return container.
117          */
118         assertEquals(Optional.of(testNode), Futures.getDone(writeTx.read(TestModel.TEST_PATH)));
119
120         DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
121
122         assertThreePhaseCommit(cohort);
123
124         assertEquals(Optional.of(testNode),
125             Futures.getDone(domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)));
126     }
127
128     @Test
129     public void testDelete() throws Exception {
130
131         DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
132         assertNotNull(writeTx);
133
134         // Write /test and commit
135
136         writeTx.write(TestModel.TEST_PATH, testContainer());
137
138         assertThreePhaseCommit(writeTx.ready());
139
140         assertTrue(Futures.getDone(domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)).isPresent());
141
142         // Delete /test and verify
143
144         writeTx = domStore.newWriteOnlyTransaction();
145
146         writeTx.delete(TestModel.TEST_PATH);
147
148         assertThreePhaseCommit(writeTx.ready());
149
150         assertEquals(Optional.empty(), Futures.getDone(domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)));
151     }
152
153     @Test
154     public void testMerge() throws Exception {
155
156         DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
157         assertNotNull(writeTx);
158
159         ContainerNode containerNode = ImmutableNodes.newContainerBuilder()
160             .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
161             .addChild(ImmutableNodes.newSystemMapBuilder()
162                 .withNodeIdentifier(new NodeIdentifier(TestModel.OUTER_LIST_QNAME))
163                 .addChild(ImmutableNodes.newMapEntryBuilder()
164                     .withNodeIdentifier(NodeIdentifierWithPredicates.of(TestModel.OUTER_LIST_QNAME,
165                         TestModel.ID_QNAME, 1))
166                     .withChild(ImmutableNodes.leafNode(TestModel.ID_QNAME, 1))
167                     .build())
168                 .build())
169             .build();
170
171         writeTx.merge(TestModel.TEST_PATH, containerNode);
172
173         assertThreePhaseCommit(writeTx.ready());
174
175         assertEquals(Optional.of(containerNode),
176             Futures.getDone(domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)));
177
178         // Merge a new list entry node
179
180         writeTx = domStore.newWriteOnlyTransaction();
181         assertNotNull(writeTx);
182
183         containerNode = ImmutableNodes.newContainerBuilder()
184             .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
185             .addChild(ImmutableNodes.newSystemMapBuilder()
186                 .withNodeIdentifier(new NodeIdentifier(TestModel.OUTER_LIST_QNAME))
187                 .addChild(ImmutableNodes.newMapEntryBuilder()
188                     .withNodeIdentifier(NodeIdentifierWithPredicates.of(TestModel.OUTER_LIST_QNAME,
189                         TestModel.ID_QNAME, 1))
190                     .withChild(ImmutableNodes.leafNode(TestModel.ID_QNAME, 1))
191                     .build())
192                 .addChild(ImmutableNodes.newMapEntryBuilder()
193                     .withNodeIdentifier(NodeIdentifierWithPredicates.of(TestModel.OUTER_LIST_QNAME,
194                         TestModel.ID_QNAME, 2))
195                     .withChild(ImmutableNodes.leafNode(TestModel.ID_QNAME, 2))
196                     .build())
197                 .build())
198             .build();
199
200         writeTx.merge(TestModel.TEST_PATH, containerNode);
201
202         assertThreePhaseCommit(writeTx.ready());
203
204         assertEquals(Optional.of(containerNode),
205             Futures.getDone(domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)));
206     }
207
208     @Test
209     public void testExistsForExistingData() throws Exception {
210
211         DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
212         assertNotNull(writeTx);
213
214         ContainerNode containerNode = ImmutableNodes.newContainerBuilder()
215             .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
216             .addChild(ImmutableNodes.newSystemMapBuilder()
217                 .withNodeIdentifier(new NodeIdentifier(TestModel.OUTER_LIST_QNAME))
218                 .addChild(ImmutableNodes.newMapEntryBuilder()
219                     .withNodeIdentifier(NodeIdentifierWithPredicates.of(TestModel.OUTER_LIST_QNAME,
220                         TestModel.ID_QNAME, 1))
221                     .withChild(ImmutableNodes.leafNode(TestModel.ID_QNAME, 1))
222                     .build())
223                 .build())
224             .build();
225
226         writeTx.merge(TestModel.TEST_PATH, containerNode);
227
228         assertEquals(Boolean.TRUE, Futures.getDone(writeTx.exists(TestModel.TEST_PATH)));
229
230         DOMStoreThreePhaseCommitCohort ready = writeTx.ready();
231
232         ready.preCommit().get();
233
234         ready.commit().get();
235
236         DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
237         assertNotNull(readTx);
238
239         assertEquals(Boolean.TRUE, Futures.getDone(readTx.exists(TestModel.TEST_PATH)));
240     }
241
242     @Test
243     public void testExistsForNonExistingData() throws Exception {
244
245         DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
246         assertNotNull(writeTx);
247
248         var exists = writeTx.exists(TestModel.TEST_PATH);
249
250         assertEquals(Boolean.FALSE, exists.get());
251
252         var readTx = domStore.newReadOnlyTransaction();
253         assertNotNull(readTx);
254
255         exists = readTx.exists(TestModel.TEST_PATH);
256
257         assertEquals(Boolean.FALSE, exists.get());
258     }
259
260     @Test
261     public void testExistsThrowsReadFailedException() {
262         var readTx = domStore.newReadOnlyTransaction();
263         assertNotNull(readTx);
264
265         readTx.close();
266
267         final var future = readTx.exists(TestModel.TEST_PATH);
268
269         final var ex = assertThrows(ExecutionException.class, future::get).getCause();
270         assertThat(ex, instanceOf(ReadFailedException.class));
271     }
272
273
274     @Test
275     public void testReadWithReadOnlyTransactionClosed() {
276         DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
277         assertNotNull(readTx);
278
279         readTx.close();
280
281         assertReadThrows(readTx);
282     }
283
284     @Test
285     public void testReadWithReadOnlyTransactionFailure() {
286         DataTreeSnapshot mockSnapshot = mock(DataTreeSnapshot.class);
287         doThrow(new RuntimeException("mock ex")).when(mockSnapshot)
288             .readNode(any(YangInstanceIdentifier.class));
289
290         DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadTransaction("1", true, mockSnapshot);
291
292         assertReadThrows(readTx);
293     }
294
295     @Test
296     public void testReadWithReadWriteTransactionClosed() {
297
298         DOMStoreReadTransaction readTx = domStore.newReadWriteTransaction();
299         assertNotNull(readTx);
300
301         readTx.close();
302
303         assertReadThrows(readTx);
304     }
305
306     @Test
307     public void testReadWithReadWriteTransactionFailure() {
308         DataTreeSnapshot mockSnapshot = mock(DataTreeSnapshot.class);
309         DataTreeModification mockModification = mock(DataTreeModification.class);
310         doThrow(new RuntimeException("mock ex")).when(mockModification)
311             .readNode(any(YangInstanceIdentifier.class));
312         doReturn(mockModification).when(mockSnapshot).newModification();
313         @SuppressWarnings("unchecked")
314         TransactionReadyPrototype<String> mockReady = mock(TransactionReadyPrototype.class);
315         DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadWriteTransaction(
316                 "1", false, mockSnapshot, mockReady);
317
318         assertReadThrows(readTx);
319     }
320
321     private static void assertReadThrows(final DOMStoreReadTransaction readTx) {
322         final var future = readTx.read(TestModel.TEST_PATH);
323         final var cause = assertThrows(ExecutionException.class, future::get).getCause();
324         assertThat(cause, instanceOf(ReadFailedException.class));
325     }
326
327     @Test
328     public void testWriteWithTransactionReady() {
329         var writeTx = domStore.newWriteOnlyTransaction();
330         writeTx.ready();
331
332         // Should throw ex
333         assertThrows(IllegalStateException.class, () -> writeTx.write(TestModel.TEST_PATH, testContainer()));
334     }
335
336     @Test
337     public void testReadyWithTransactionAlreadyReady() {
338         var writeTx = domStore.newWriteOnlyTransaction();
339
340         writeTx.ready();
341
342         // Should throw ex
343         assertThrows(IllegalStateException.class, writeTx::ready);
344     }
345
346     @Test
347     public void testReadyWithMissingMandatoryData() throws Exception {
348         DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
349         var testNode = ImmutableNodes.newContainerBuilder()
350                 .withNodeIdentifier(new NodeIdentifier(TestModel.MANDATORY_DATA_TEST_QNAME))
351                 .addChild(ImmutableNodes.leafNode(TestModel.OPTIONAL_QNAME, "data"))
352                 .build();
353         writeTx.write(TestModel.MANDATORY_DATA_TEST_PATH, testNode);
354         DOMStoreThreePhaseCommitCohort ready = writeTx.ready();
355
356         final var future = ready.canCommit();
357         final var cause = assertThrows(ExecutionException.class, future::get).getCause();
358         assertThat(cause, instanceOf(IllegalArgumentException.class));
359         assertThat(cause.getMessage(), containsString("mandatory-data-test is missing mandatory descendant"));
360     }
361
362     @Test
363     public void testTransactionAbort() throws Exception {
364
365         DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
366         assertNotNull(writeTx);
367
368         assertTestContainerWrite(writeTx);
369
370         DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
371
372         assertTrue(cohort.canCommit().get());
373         cohort.preCommit().get();
374         cohort.abort().get();
375
376         assertEquals(Optional.empty(), domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get());
377     }
378
379     @Test
380     public void testTransactionChain() throws Exception {
381         DOMStoreTransactionChain txChain = domStore.createTransactionChain();
382         assertNotNull(txChain);
383
384         /**
385          * We alocate new read-write transaction and write /test.
386          */
387         DOMStoreReadWriteTransaction firstTx = txChain.newReadWriteTransaction();
388         assertTestContainerWrite(firstTx);
389
390         /**
391          * First transaction is marked as ready, we are able to allocate chained
392          * transactions.
393          */
394         final DOMStoreThreePhaseCommitCohort firstWriteTxCohort = firstTx.ready();
395
396         /**
397          * We alocate chained transaction - read transaction, note first one is
398          * still not commited to datastore.
399          */
400         DOMStoreReadTransaction secondReadTx = txChain.newReadOnlyTransaction();
401
402         /**
403          * We test if we are able to read data from tx, read should not fail
404          * since we are using chained transaction.
405          */
406         assertTestContainerExists(secondReadTx);
407
408         /**
409          * We alocate next transaction, which is still based on first one, but
410          * is read-write.
411          */
412         DOMStoreReadWriteTransaction thirdDeleteTx = txChain.newReadWriteTransaction();
413
414         /**
415          * We test existence of /test in third transaction container should
416          * still be visible from first one (which is still uncommmited).
417          */
418         assertTestContainerExists(thirdDeleteTx);
419
420         /**
421          * We delete node in third transaction.
422          */
423         thirdDeleteTx.delete(TestModel.TEST_PATH);
424
425         /**
426          * third transaction is sealed.
427          */
428         DOMStoreThreePhaseCommitCohort thirdDeleteTxCohort = thirdDeleteTx.ready();
429
430         /**
431          * We commit first transaction.
432          *
433          */
434         assertThreePhaseCommit(firstWriteTxCohort);
435
436         // Alocates store transacion
437         DOMStoreReadTransaction storeReadTx = domStore.newReadOnlyTransaction();
438         /**
439          * We verify transaction is commited to store, container should exists
440          * in datastore.
441          */
442         assertTestContainerExists(storeReadTx);
443         /**
444          * We commit third transaction
445          *
446          */
447         assertThreePhaseCommit(thirdDeleteTxCohort);
448     }
449
450     @Test
451     @Ignore
452     public void testTransactionConflict() throws Exception {
453         DOMStoreReadWriteTransaction txOne = domStore.newReadWriteTransaction();
454         DOMStoreReadWriteTransaction txTwo = domStore.newReadWriteTransaction();
455         assertTestContainerWrite(txOne);
456         assertTestContainerWrite(txTwo);
457
458         /**
459          * Commits transaction
460          */
461         assertThreePhaseCommit(txOne.ready());
462
463         /**
464          * Asserts that txTwo could not be commited
465          */
466         assertFalse(txTwo.ready().canCommit().get());
467     }
468
469     private static void assertThreePhaseCommit(final DOMStoreThreePhaseCommitCohort cohort) throws Exception {
470         assertTrue(cohort.canCommit().get());
471         cohort.preCommit().get();
472         cohort.commit().get();
473     }
474
475     private static Optional<NormalizedNode> assertTestContainerWrite(final DOMStoreReadWriteTransaction writeTx)
476             throws Exception {
477         /**
478          *
479          * Writes /test in writeTx
480          *
481          */
482         writeTx.write(TestModel.TEST_PATH, testContainer());
483
484         return assertTestContainerExists(writeTx);
485     }
486
487     /**
488      * Reads /test from readTx Read should return container.
489      */
490     private static Optional<NormalizedNode> assertTestContainerExists(final DOMStoreReadTransaction readTx)
491             throws Exception {
492
493         ListenableFuture<Optional<NormalizedNode>> writeTxContainer = readTx.read(TestModel.TEST_PATH);
494         assertTrue(writeTxContainer.get().isPresent());
495         return writeTxContainer.get();
496     }
497 }