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