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