Bug 1392: Change ReadTransaction#read to return CheckedFuture
[controller.git] / opendaylight / md-sal / sal-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 java.util.concurrent.ExecutionException;
16
17 import org.junit.Before;
18 import org.junit.Ignore;
19 import org.junit.Test;
20 import org.mockito.Mockito;
21 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
22 import org.opendaylight.controller.md.sal.dom.store.impl.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
23 import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
24 import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
25 import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
26 import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
27 import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
30 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
32 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
33 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
34 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
35 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
36 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
37
38 import com.google.common.base.Optional;
39 import com.google.common.util.concurrent.ListenableFuture;
40 import com.google.common.util.concurrent.MoreExecutors;
41
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.sameThreadExecutor());
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 = domStore.newReadOnlyTransaction().
128                 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().
140                 read(TestModel.TEST_PATH ).get();
141         assertEquals( "After commit read: isPresent", false, afterCommitRead.isPresent() );
142     }
143
144     @Test
145     public void testMerge() throws Exception {
146
147         DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
148         assertNotNull( writeTx );
149
150         ContainerNode containerNode = ImmutableContainerNodeBuilder.create()
151                 .withNodeIdentifier( new NodeIdentifier( TestModel.TEST_QNAME ) )
152                 .addChild( ImmutableNodes.mapNodeBuilder( TestModel.OUTER_LIST_QNAME )
153                         .addChild( ImmutableNodes.mapEntry( TestModel.OUTER_LIST_QNAME,
154                                                             TestModel.ID_QNAME, 1 ) ).build() ).build();
155
156         writeTx.merge( TestModel.TEST_PATH, containerNode );
157
158         assertThreePhaseCommit( writeTx.ready() );
159
160         Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().
161                 read(TestModel.TEST_PATH ).get();
162         assertEquals( "After commit read: isPresent", true, afterCommitRead.isPresent() );
163         assertEquals( "After commit read: data", containerNode, afterCommitRead.get() );
164
165         // Merge a new list entry node
166
167         writeTx = domStore.newWriteOnlyTransaction();
168         assertNotNull( writeTx );
169
170         containerNode = ImmutableContainerNodeBuilder.create()
171                 .withNodeIdentifier( new NodeIdentifier( TestModel.TEST_QNAME ) )
172                 .addChild( ImmutableNodes.mapNodeBuilder( TestModel.OUTER_LIST_QNAME )
173                         .addChild( ImmutableNodes.mapEntry( TestModel.OUTER_LIST_QNAME,
174                                                             TestModel.ID_QNAME, 1 ) )
175                         .addChild( ImmutableNodes.mapEntry( TestModel.OUTER_LIST_QNAME,
176                                                             TestModel.ID_QNAME, 2 ) ).build() ).build();
177
178         writeTx.merge( TestModel.TEST_PATH, containerNode );
179
180         assertThreePhaseCommit( writeTx.ready() );
181
182         afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH ).get();
183         assertEquals( "After commit read: isPresent", true, afterCommitRead.isPresent() );
184         assertEquals( "After commit read: data", containerNode, afterCommitRead.get() );
185     }
186
187     @Test(expected=ReadFailedException.class)
188     public void testReadWithReadOnlyTransactionClosed() throws Throwable {
189
190         DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
191         assertNotNull( readTx );
192
193         readTx.close();
194
195         doReadAndThrowEx( readTx );
196     }
197
198     @Test(expected=ReadFailedException.class)
199     public void testReadWithReadOnlyTransactionFailure() throws Throwable {
200
201         DataTreeSnapshot mockSnapshot = Mockito.mock( DataTreeSnapshot.class );
202         Mockito.doThrow( new RuntimeException( "mock ex" ) ).when( mockSnapshot )
203         .readNode( Mockito.any( YangInstanceIdentifier.class ) );
204
205         DOMStoreReadTransaction readTx = new SnapshotBackedReadTransaction( "1", mockSnapshot );
206
207         doReadAndThrowEx( readTx );
208     }
209
210     @Test(expected=ReadFailedException.class)
211     public void testReadWithReadWriteTransactionClosed() throws Throwable {
212
213         DOMStoreReadTransaction readTx = domStore.newReadWriteTransaction();
214         assertNotNull( readTx );
215
216         readTx.close();
217
218         doReadAndThrowEx( readTx );
219     }
220
221     @Test(expected=ReadFailedException.class)
222     public void testReadWithReadWriteTransactionFailure() throws Throwable {
223
224         DataTreeSnapshot mockSnapshot = Mockito.mock( DataTreeSnapshot.class );
225         DataTreeModification mockModification = Mockito.mock( DataTreeModification.class );
226         Mockito.doThrow( new RuntimeException( "mock ex" ) ).when( mockModification )
227         .readNode( Mockito.any( YangInstanceIdentifier.class ) );
228         Mockito.doReturn( mockModification ).when( mockSnapshot ).newModification();
229         TransactionReadyPrototype mockReady = Mockito.mock( TransactionReadyPrototype.class );
230         DOMStoreReadTransaction readTx = new SnapshotBackedReadWriteTransaction( "1", mockSnapshot, mockReady );
231
232         doReadAndThrowEx( readTx );
233     }
234
235     private void doReadAndThrowEx( DOMStoreReadTransaction readTx ) throws Throwable {
236
237         try {
238             readTx.read(TestModel.TEST_PATH).get();
239         } catch( ExecutionException e ) {
240             throw e.getCause();
241         }
242     }
243
244     @Test(expected=IllegalStateException.class)
245     public void testWriteWithTransactionReady() throws Exception {
246
247         DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
248
249         writeTx.ready();
250
251         // Should throw ex
252         writeTx.write( TestModel.TEST_PATH, ImmutableNodes.containerNode( TestModel.TEST_QNAME ) );
253     }
254
255     @Test(expected=IllegalStateException.class)
256     public void testReadyWithTransactionAlreadyReady() throws Exception {
257
258         DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
259
260         writeTx.ready();
261
262         // Should throw ex
263         writeTx.ready();
264     }
265
266     @Test
267     public void testTransactionAbort() throws InterruptedException, ExecutionException {
268
269         DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
270         assertNotNull(writeTx);
271
272         assertTestContainerWrite(writeTx);
273
274         DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
275
276         assertTrue(cohort.canCommit().get().booleanValue());
277         cohort.preCommit().get();
278         cohort.abort().get();
279
280         Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)
281                 .get();
282         assertFalse(afterCommitRead.isPresent());
283     }
284
285     @Test
286     public void testTransactionChain() throws InterruptedException, ExecutionException {
287         DOMStoreTransactionChain txChain = domStore.createTransactionChain();
288         assertNotNull(txChain);
289
290         /**
291          * We alocate new read-write transaction and write /test
292          *
293          *
294          */
295         DOMStoreReadWriteTransaction firstTx = txChain.newReadWriteTransaction();
296         assertTestContainerWrite(firstTx);
297
298         /**
299          * First transaction is marked as ready, we are able to allocate chained
300          * transactions
301          */
302         DOMStoreThreePhaseCommitCohort firstWriteTxCohort = firstTx.ready();
303
304         /**
305          * We alocate chained transaction - read transaction, note first one is
306          * still not commited to datastore.
307          */
308         DOMStoreReadTransaction secondReadTx = txChain.newReadOnlyTransaction();
309
310         /**
311          *
312          * We test if we are able to read data from tx, read should not fail
313          * since we are using chained transaction.
314          *
315          *
316          */
317         assertTestContainerExists(secondReadTx);
318
319         /**
320          *
321          * We alocate next transaction, which is still based on first one, but
322          * is read-write.
323          *
324          */
325         DOMStoreReadWriteTransaction thirdDeleteTx = txChain.newReadWriteTransaction();
326
327         /**
328          * We test existence of /test in third transaction container should
329          * still be visible from first one (which is still uncommmited).
330          *
331          *
332          */
333         assertTestContainerExists(thirdDeleteTx);
334
335         /**
336          * We delete node in third transaction
337          */
338         thirdDeleteTx.delete(TestModel.TEST_PATH);
339
340         /**
341          * third transaction is sealed.
342          */
343         DOMStoreThreePhaseCommitCohort thirdDeleteTxCohort = thirdDeleteTx.ready();
344
345         /**
346          * We commit first transaction
347          *
348          */
349         assertThreePhaseCommit(firstWriteTxCohort);
350
351         // Alocates store transacion
352         DOMStoreReadTransaction storeReadTx = domStore.newReadOnlyTransaction();
353         /**
354          * We verify transaction is commited to store, container should exists
355          * in datastore.
356          */
357         assertTestContainerExists(storeReadTx);
358         /**
359          * We commit third transaction
360          *
361          */
362         assertThreePhaseCommit(thirdDeleteTxCohort);
363     }
364
365     @Test
366     @Ignore
367     public void testTransactionConflict() throws InterruptedException, ExecutionException {
368         DOMStoreReadWriteTransaction txOne = domStore.newReadWriteTransaction();
369         DOMStoreReadWriteTransaction txTwo = domStore.newReadWriteTransaction();
370         assertTestContainerWrite(txOne);
371         assertTestContainerWrite(txTwo);
372
373         /**
374          * Commits transaction
375          */
376         assertThreePhaseCommit(txOne.ready());
377
378         /**
379          * Asserts that txTwo could not be commited
380          */
381         assertFalse(txTwo.ready().canCommit().get());
382     }
383
384     private static void assertThreePhaseCommit(final DOMStoreThreePhaseCommitCohort cohort)
385             throws InterruptedException, ExecutionException {
386         assertTrue(cohort.canCommit().get().booleanValue());
387         cohort.preCommit().get();
388         cohort.commit().get();
389     }
390
391     private static Optional<NormalizedNode<?, ?>> assertTestContainerWrite(final DOMStoreReadWriteTransaction writeTx)
392             throws InterruptedException, ExecutionException {
393         /**
394          *
395          * Writes /test in writeTx
396          *
397          */
398         writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
399
400         return assertTestContainerExists(writeTx);
401     }
402
403     /**
404      * Reads /test from readTx Read should return container.
405      */
406     private static Optional<NormalizedNode<?, ?>> assertTestContainerExists(final DOMStoreReadTransaction readTx)
407             throws InterruptedException, ExecutionException {
408
409         ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = readTx.read(TestModel.TEST_PATH);
410         assertTrue(writeTxContainer.get().isPresent());
411         return writeTxContainer.get();
412     }
413
414 }