Migrate Optional.get() callers
[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.schema.ContainerNode;
44 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
45 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
46 import org.opendaylight.yangtools.yang.data.impl.schema.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         NormalizedNode testNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
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     @Test
98     public void testTransactionCommit() throws Exception {
99
100         DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
101         assertNotNull(writeTx);
102
103         /**
104          * Writes /test in writeTx.
105          */
106         NormalizedNode testNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
107         writeTx.write(TestModel.TEST_PATH, testNode);
108
109         /**
110          * Reads /test from writeTx Read should return container.
111          */
112         assertEquals(Optional.of(testNode), Futures.getDone(writeTx.read(TestModel.TEST_PATH)));
113
114         DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
115
116         assertThreePhaseCommit(cohort);
117
118         assertEquals(Optional.of(testNode),
119             Futures.getDone(domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)));
120     }
121
122     @Test
123     public void testDelete() throws Exception {
124
125         DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
126         assertNotNull(writeTx);
127
128         // Write /test and commit
129
130         writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
131
132         assertThreePhaseCommit(writeTx.ready());
133
134         assertTrue(Futures.getDone(domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)).isPresent());
135
136         // Delete /test and verify
137
138         writeTx = domStore.newWriteOnlyTransaction();
139
140         writeTx.delete(TestModel.TEST_PATH);
141
142         assertThreePhaseCommit(writeTx.ready());
143
144         assertEquals(Optional.empty(), Futures.getDone(domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)));
145     }
146
147     @Test
148     public void testMerge() throws Exception {
149
150         DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
151         assertNotNull(writeTx);
152
153         ContainerNode containerNode = Builders.containerBuilder()
154                 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
155                 .addChild(ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
156                         .addChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME,
157                                                             TestModel.ID_QNAME, 1)).build()).build();
158
159         writeTx.merge(TestModel.TEST_PATH, containerNode);
160
161         assertThreePhaseCommit(writeTx.ready());
162
163         assertEquals(Optional.of(containerNode),
164             Futures.getDone(domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)));
165
166         // Merge a new list entry node
167
168         writeTx = domStore.newWriteOnlyTransaction();
169         assertNotNull(writeTx);
170
171         containerNode = Builders.containerBuilder()
172             .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
173             .addChild(ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
174                 .addChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1))
175                 .addChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2))
176                 .build())
177             .build();
178
179         writeTx.merge(TestModel.TEST_PATH, containerNode);
180
181         assertThreePhaseCommit(writeTx.ready());
182
183         assertEquals(Optional.of(containerNode),
184             Futures.getDone(domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)));
185     }
186
187     @Test
188     public void testExistsForExistingData() throws Exception {
189
190         DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
191         assertNotNull(writeTx);
192
193         ContainerNode containerNode = Builders.containerBuilder()
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();
198
199         writeTx.merge(TestModel.TEST_PATH, containerNode);
200
201         assertEquals(Boolean.TRUE, Futures.getDone(writeTx.exists(TestModel.TEST_PATH)));
202
203         DOMStoreThreePhaseCommitCohort ready = writeTx.ready();
204
205         ready.preCommit().get();
206
207         ready.commit().get();
208
209         DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
210         assertNotNull(readTx);
211
212         assertEquals(Boolean.TRUE, Futures.getDone(readTx.exists(TestModel.TEST_PATH)));
213     }
214
215     @Test
216     public void testExistsForNonExistingData() throws Exception {
217
218         DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
219         assertNotNull(writeTx);
220
221         var exists = writeTx.exists(TestModel.TEST_PATH);
222
223         assertEquals(Boolean.FALSE, exists.get());
224
225         var readTx = domStore.newReadOnlyTransaction();
226         assertNotNull(readTx);
227
228         exists = readTx.exists(TestModel.TEST_PATH);
229
230         assertEquals(Boolean.FALSE, exists.get());
231     }
232
233     @Test
234     public void testExistsThrowsReadFailedException() {
235         var readTx = domStore.newReadOnlyTransaction();
236         assertNotNull(readTx);
237
238         readTx.close();
239
240         final var future = readTx.exists(TestModel.TEST_PATH);
241
242         final var ex = assertThrows(ExecutionException.class, future::get).getCause();
243         assertThat(ex, instanceOf(ReadFailedException.class));
244     }
245
246
247     @Test
248     public void testReadWithReadOnlyTransactionClosed() {
249         DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
250         assertNotNull(readTx);
251
252         readTx.close();
253
254         assertReadThrows(readTx);
255     }
256
257     @Test
258     public void testReadWithReadOnlyTransactionFailure() {
259         DataTreeSnapshot mockSnapshot = mock(DataTreeSnapshot.class);
260         doThrow(new RuntimeException("mock ex")).when(mockSnapshot)
261             .readNode(any(YangInstanceIdentifier.class));
262
263         DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadTransaction("1", true, mockSnapshot);
264
265         assertReadThrows(readTx);
266     }
267
268     @Test
269     public void testReadWithReadWriteTransactionClosed() {
270
271         DOMStoreReadTransaction readTx = domStore.newReadWriteTransaction();
272         assertNotNull(readTx);
273
274         readTx.close();
275
276         assertReadThrows(readTx);
277     }
278
279     @Test
280     public void testReadWithReadWriteTransactionFailure() {
281         DataTreeSnapshot mockSnapshot = mock(DataTreeSnapshot.class);
282         DataTreeModification mockModification = mock(DataTreeModification.class);
283         doThrow(new RuntimeException("mock ex")).when(mockModification)
284             .readNode(any(YangInstanceIdentifier.class));
285         doReturn(mockModification).when(mockSnapshot).newModification();
286         @SuppressWarnings("unchecked")
287         TransactionReadyPrototype<String> mockReady = mock(TransactionReadyPrototype.class);
288         DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadWriteTransaction(
289                 "1", false, mockSnapshot, mockReady);
290
291         assertReadThrows(readTx);
292     }
293
294     private static void assertReadThrows(final DOMStoreReadTransaction readTx) {
295         final var future = readTx.read(TestModel.TEST_PATH);
296         final var cause = assertThrows(ExecutionException.class, future::get).getCause();
297         assertThat(cause, instanceOf(ReadFailedException.class));
298     }
299
300     @Test
301     public void testWriteWithTransactionReady() {
302         var writeTx = domStore.newWriteOnlyTransaction();
303         writeTx.ready();
304
305         // Should throw ex
306         assertThrows(IllegalStateException.class,
307             () -> writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME)));
308     }
309
310     @Test
311     public void testReadyWithTransactionAlreadyReady() {
312         var writeTx = domStore.newWriteOnlyTransaction();
313
314         writeTx.ready();
315
316         // Should throw ex
317         assertThrows(IllegalStateException.class, writeTx::ready);
318     }
319
320     @Test
321     public void testReadyWithMissingMandatoryData() throws Exception {
322         DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
323         NormalizedNode testNode = Builders.containerBuilder()
324                 .withNodeIdentifier(new NodeIdentifier(TestModel.MANDATORY_DATA_TEST_QNAME))
325                 .addChild(ImmutableNodes.leafNode(TestModel.OPTIONAL_QNAME, "data"))
326                 .build();
327         writeTx.write(TestModel.MANDATORY_DATA_TEST_PATH, testNode);
328         DOMStoreThreePhaseCommitCohort ready = writeTx.ready();
329
330         final var future = ready.canCommit();
331         final var cause = assertThrows(ExecutionException.class, future::get).getCause();
332         assertThat(cause, instanceOf(IllegalArgumentException.class));
333         assertThat(cause.getMessage(), containsString("mandatory-data-test is missing mandatory descendant"));
334     }
335
336     @Test
337     public void testTransactionAbort() throws Exception {
338
339         DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
340         assertNotNull(writeTx);
341
342         assertTestContainerWrite(writeTx);
343
344         DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
345
346         assertTrue(cohort.canCommit().get());
347         cohort.preCommit().get();
348         cohort.abort().get();
349
350         assertEquals(Optional.empty(), domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH).get());
351     }
352
353     @Test
354     public void testTransactionChain() throws Exception {
355         DOMStoreTransactionChain txChain = domStore.createTransactionChain();
356         assertNotNull(txChain);
357
358         /**
359          * We alocate new read-write transaction and write /test.
360          */
361         DOMStoreReadWriteTransaction firstTx = txChain.newReadWriteTransaction();
362         assertTestContainerWrite(firstTx);
363
364         /**
365          * First transaction is marked as ready, we are able to allocate chained
366          * transactions.
367          */
368         final DOMStoreThreePhaseCommitCohort firstWriteTxCohort = firstTx.ready();
369
370         /**
371          * We alocate chained transaction - read transaction, note first one is
372          * still not commited to datastore.
373          */
374         DOMStoreReadTransaction secondReadTx = txChain.newReadOnlyTransaction();
375
376         /**
377          * We test if we are able to read data from tx, read should not fail
378          * since we are using chained transaction.
379          */
380         assertTestContainerExists(secondReadTx);
381
382         /**
383          * We alocate next transaction, which is still based on first one, but
384          * is read-write.
385          */
386         DOMStoreReadWriteTransaction thirdDeleteTx = txChain.newReadWriteTransaction();
387
388         /**
389          * We test existence of /test in third transaction container should
390          * still be visible from first one (which is still uncommmited).
391          */
392         assertTestContainerExists(thirdDeleteTx);
393
394         /**
395          * We delete node in third transaction.
396          */
397         thirdDeleteTx.delete(TestModel.TEST_PATH);
398
399         /**
400          * third transaction is sealed.
401          */
402         DOMStoreThreePhaseCommitCohort thirdDeleteTxCohort = thirdDeleteTx.ready();
403
404         /**
405          * We commit first transaction.
406          *
407          */
408         assertThreePhaseCommit(firstWriteTxCohort);
409
410         // Alocates store transacion
411         DOMStoreReadTransaction storeReadTx = domStore.newReadOnlyTransaction();
412         /**
413          * We verify transaction is commited to store, container should exists
414          * in datastore.
415          */
416         assertTestContainerExists(storeReadTx);
417         /**
418          * We commit third transaction
419          *
420          */
421         assertThreePhaseCommit(thirdDeleteTxCohort);
422     }
423
424     @Test
425     @Ignore
426     public void testTransactionConflict() throws Exception {
427         DOMStoreReadWriteTransaction txOne = domStore.newReadWriteTransaction();
428         DOMStoreReadWriteTransaction txTwo = domStore.newReadWriteTransaction();
429         assertTestContainerWrite(txOne);
430         assertTestContainerWrite(txTwo);
431
432         /**
433          * Commits transaction
434          */
435         assertThreePhaseCommit(txOne.ready());
436
437         /**
438          * Asserts that txTwo could not be commited
439          */
440         assertFalse(txTwo.ready().canCommit().get());
441     }
442
443     private static void assertThreePhaseCommit(final DOMStoreThreePhaseCommitCohort cohort) throws Exception {
444         assertTrue(cohort.canCommit().get());
445         cohort.preCommit().get();
446         cohort.commit().get();
447     }
448
449     private static Optional<NormalizedNode> assertTestContainerWrite(final DOMStoreReadWriteTransaction writeTx)
450             throws Exception {
451         /**
452          *
453          * Writes /test in writeTx
454          *
455          */
456         writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
457
458         return assertTestContainerExists(writeTx);
459     }
460
461     /**
462      * Reads /test from readTx Read should return container.
463      */
464     private static Optional<NormalizedNode> assertTestContainerExists(final DOMStoreReadTransaction readTx)
465             throws Exception {
466
467         ListenableFuture<Optional<NormalizedNode>> writeTxContainer = readTx.read(TestModel.TEST_PATH);
468         assertTrue(writeTxContainer.get().isPresent());
469         return writeTxContainer.get();
470     }
471 }