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