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