Cleaned up mdsal-common-api and mdsal-dom-spi
[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 org.opendaylight.mdsal.dom.spi.store.DOMStoreReadTransaction;
16 import org.opendaylight.mdsal.dom.spi.store.DOMStoreReadWriteTransaction;
17 import org.opendaylight.mdsal.dom.spi.store.DOMStoreThreePhaseCommitCohort;
18 import org.opendaylight.mdsal.dom.spi.store.DOMStoreTransactionChain;
19 import org.opendaylight.mdsal.dom.spi.store.DOMStoreWriteTransaction;
20 import org.opendaylight.mdsal.dom.spi.store.SnapshotBackedTransactions;
21 import org.opendaylight.mdsal.dom.spi.store.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
22
23 import org.opendaylight.mdsal.common.api.ReadFailedException;
24 import com.google.common.base.Optional;
25 import com.google.common.util.concurrent.CheckedFuture;
26 import com.google.common.util.concurrent.ListenableFuture;
27 import com.google.common.util.concurrent.MoreExecutors;
28 import java.util.concurrent.ExecutionException;
29 import org.junit.Before;
30 import org.junit.Ignore;
31 import org.junit.Test;
32 import org.mockito.Mockito;
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 = 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
188     @Test
189     public void testExistsForExistingData() throws Exception {
190
191         DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
192         assertNotNull( writeTx );
193
194         ContainerNode containerNode = ImmutableContainerNodeBuilder.create()
195             .withNodeIdentifier( new NodeIdentifier( TestModel.TEST_QNAME ) )
196             .addChild( ImmutableNodes.mapNodeBuilder( TestModel.OUTER_LIST_QNAME )
197                 .addChild( ImmutableNodes.mapEntry( TestModel.OUTER_LIST_QNAME,
198                     TestModel.ID_QNAME, 1 ) ).build() ).build();
199
200         writeTx.merge( TestModel.TEST_PATH, containerNode );
201
202         CheckedFuture<Boolean, ReadFailedException> exists =
203             writeTx.exists(TestModel.TEST_PATH);
204
205         assertEquals(true, exists.checkedGet());
206
207         DOMStoreThreePhaseCommitCohort ready = writeTx.ready();
208
209         ready.preCommit().get();
210
211         ready.commit().get();
212
213         DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
214         assertNotNull( readTx );
215
216         exists =
217             readTx.exists(TestModel.TEST_PATH);
218
219         assertEquals(true, exists.checkedGet());
220     }
221
222     @Test
223     public void testExistsForNonExistingData() throws Exception {
224
225         DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
226         assertNotNull( writeTx );
227
228         CheckedFuture<Boolean, ReadFailedException> exists =
229             writeTx.exists(TestModel.TEST_PATH);
230
231         assertEquals(false, exists.checkedGet());
232
233         DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
234         assertNotNull( readTx );
235
236         exists =
237             readTx.exists(TestModel.TEST_PATH);
238
239         assertEquals(false, exists.checkedGet());
240     }
241
242     @Test(expected=ReadFailedException.class)
243     public void testExistsThrowsReadFailedException() throws Exception {
244
245         DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
246         assertNotNull( readTx );
247
248         readTx.close();
249
250         readTx.exists(TestModel.TEST_PATH).checkedGet();
251     }
252
253
254
255     @Test(expected=ReadFailedException.class)
256     public void testReadWithReadOnlyTransactionClosed() throws Throwable {
257
258         DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
259         assertNotNull( readTx );
260
261         readTx.close();
262
263         doReadAndThrowEx( readTx );
264     }
265
266     @Test(expected=ReadFailedException.class)
267     public void testReadWithReadOnlyTransactionFailure() throws Throwable {
268
269         DataTreeSnapshot mockSnapshot = Mockito.mock( DataTreeSnapshot.class );
270         Mockito.doThrow( new RuntimeException( "mock ex" ) ).when( mockSnapshot )
271         .readNode( Mockito.any( YangInstanceIdentifier.class ) );
272
273         DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadTransaction("1", true, mockSnapshot);
274
275         doReadAndThrowEx( readTx );
276     }
277
278     @Test(expected=ReadFailedException.class)
279     public void testReadWithReadWriteTransactionClosed() throws Throwable {
280
281         DOMStoreReadTransaction readTx = domStore.newReadWriteTransaction();
282         assertNotNull( readTx );
283
284         readTx.close();
285
286         doReadAndThrowEx( readTx );
287     }
288
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("1", false, mockSnapshot, mockReady);
300
301         doReadAndThrowEx( readTx );
302     }
303
304     private static void doReadAndThrowEx( final DOMStoreReadTransaction readTx ) throws Throwable {
305         try {
306             readTx.read(TestModel.TEST_PATH).get();
307         } catch( ExecutionException e ) {
308             throw e.getCause();
309         }
310     }
311
312     @Test(expected=IllegalStateException.class)
313     public void testWriteWithTransactionReady() throws Exception {
314
315         DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
316
317         writeTx.ready();
318
319         // Should throw ex
320         writeTx.write( TestModel.TEST_PATH, ImmutableNodes.containerNode( TestModel.TEST_QNAME ) );
321     }
322
323     @Test(expected=IllegalStateException.class)
324     public void testReadyWithTransactionAlreadyReady() throws Exception {
325
326         DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
327
328         writeTx.ready();
329
330         // Should throw ex
331         writeTx.ready();
332     }
333
334     @Test
335     public void testTransactionAbort() throws InterruptedException, ExecutionException {
336
337         DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
338         assertNotNull(writeTx);
339
340         assertTestContainerWrite(writeTx);
341
342         DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
343
344         assertTrue(cohort.canCommit().get().booleanValue());
345         cohort.preCommit().get();
346         cohort.abort().get();
347
348         Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)
349                 .get();
350         assertFalse(afterCommitRead.isPresent());
351     }
352
353     @Test
354     public void testTransactionChain() throws InterruptedException, ExecutionException {
355         DOMStoreTransactionChain txChain = domStore.createTransactionChain();
356         assertNotNull(txChain);
357
358         /**
359          * We alocate new read-write transaction and write /test
360          *
361          *
362          */
363         DOMStoreReadWriteTransaction firstTx = txChain.newReadWriteTransaction();
364         assertTestContainerWrite(firstTx);
365
366         /**
367          * First transaction is marked as ready, we are able to allocate chained
368          * transactions
369          */
370         DOMStoreThreePhaseCommitCohort firstWriteTxCohort = firstTx.ready();
371
372         /**
373          * We alocate chained transaction - read transaction, note first one is
374          * still not commited to datastore.
375          */
376         DOMStoreReadTransaction secondReadTx = txChain.newReadOnlyTransaction();
377
378         /**
379          *
380          * We test if we are able to read data from tx, read should not fail
381          * since we are using chained transaction.
382          *
383          *
384          */
385         assertTestContainerExists(secondReadTx);
386
387         /**
388          *
389          * We alocate next transaction, which is still based on first one, but
390          * is read-write.
391          *
392          */
393         DOMStoreReadWriteTransaction thirdDeleteTx = txChain.newReadWriteTransaction();
394
395         /**
396          * We test existence of /test in third transaction container should
397          * still be visible from first one (which is still uncommmited).
398          *
399          *
400          */
401         assertTestContainerExists(thirdDeleteTx);
402
403         /**
404          * We delete node in third transaction
405          */
406         thirdDeleteTx.delete(TestModel.TEST_PATH);
407
408         /**
409          * third transaction is sealed.
410          */
411         DOMStoreThreePhaseCommitCohort thirdDeleteTxCohort = thirdDeleteTx.ready();
412
413         /**
414          * We commit first transaction
415          *
416          */
417         assertThreePhaseCommit(firstWriteTxCohort);
418
419         // Alocates store transacion
420         DOMStoreReadTransaction storeReadTx = domStore.newReadOnlyTransaction();
421         /**
422          * We verify transaction is commited to store, container should exists
423          * in datastore.
424          */
425         assertTestContainerExists(storeReadTx);
426         /**
427          * We commit third transaction
428          *
429          */
430         assertThreePhaseCommit(thirdDeleteTxCohort);
431     }
432
433     @Test
434     @Ignore
435     public void testTransactionConflict() throws InterruptedException, ExecutionException {
436         DOMStoreReadWriteTransaction txOne = domStore.newReadWriteTransaction();
437         DOMStoreReadWriteTransaction txTwo = domStore.newReadWriteTransaction();
438         assertTestContainerWrite(txOne);
439         assertTestContainerWrite(txTwo);
440
441         /**
442          * Commits transaction
443          */
444         assertThreePhaseCommit(txOne.ready());
445
446         /**
447          * Asserts that txTwo could not be commited
448          */
449         assertFalse(txTwo.ready().canCommit().get());
450     }
451
452     private static void assertThreePhaseCommit(final DOMStoreThreePhaseCommitCohort cohort)
453             throws InterruptedException, ExecutionException {
454         assertTrue(cohort.canCommit().get().booleanValue());
455         cohort.preCommit().get();
456         cohort.commit().get();
457     }
458
459     private static Optional<NormalizedNode<?, ?>> assertTestContainerWrite(final DOMStoreReadWriteTransaction writeTx)
460             throws InterruptedException, ExecutionException {
461         /**
462          *
463          * Writes /test in writeTx
464          *
465          */
466         writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
467
468         return assertTestContainerExists(writeTx);
469     }
470
471     /**
472      * Reads /test from readTx Read should return container.
473      */
474     private static Optional<NormalizedNode<?, ?>> assertTestContainerExists(final DOMStoreReadTransaction readTx)
475             throws InterruptedException, ExecutionException {
476
477         ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = readTx.read(TestModel.TEST_PATH);
478         assertTrue(writeTxContainer.get().isPresent());
479         return writeTxContainer.get();
480     }
481
482 }