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