Migrate OSGI compendium reference
[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 import java.util.concurrent.ExecutionException;
20 import org.junit.Assert;
21 import org.junit.Before;
22 import org.junit.Ignore;
23 import org.junit.Test;
24 import org.mockito.Mockito;
25 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
26 import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
27 import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
28 import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
29 import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
30 import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
31 import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedTransactions;
32 import org.opendaylight.controller.sal.core.spi.data.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
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().read(TestModel.TEST_PATH)
128                 .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().read(TestModel.TEST_PATH).get();
140         assertEquals("After commit read: isPresent", false, afterCommitRead.isPresent());
141     }
142
143     @Test
144     public void testMerge() throws Exception {
145
146         DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
147         assertNotNull(writeTx);
148
149         ContainerNode containerNode = ImmutableContainerNodeBuilder.create()
150                 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
151                 .addChild(ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
152                         .addChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1)).build())
153                 .build();
154
155         writeTx.merge(TestModel.TEST_PATH, containerNode);
156
157         assertThreePhaseCommit(writeTx.ready());
158
159         Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)
160                 .get();
161         assertEquals("After commit read: isPresent", true, afterCommitRead.isPresent());
162         assertEquals("After commit read: data", containerNode, afterCommitRead.get());
163
164         // Merge a new list entry node
165
166         writeTx = domStore.newWriteOnlyTransaction();
167         assertNotNull(writeTx);
168
169         containerNode = ImmutableContainerNodeBuilder.create()
170                 .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
171                 .addChild(ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
172                         .addChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1))
173                         .addChild(ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2)).build())
174                 .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, TestModel.ID_QNAME, 1)).build())
196                 .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     @Test(expected = ReadFailedException.class)
252     public void testReadWithReadOnlyTransactionClosed() throws Exception {
253
254         DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
255         assertNotNull(readTx);
256
257         readTx.close();
258
259         doReadAndThrowEx(readTx);
260     }
261
262     @Test(expected = ReadFailedException.class)
263     public void testReadWithReadOnlyTransactionFailure() throws Exception {
264
265         DataTreeSnapshot mockSnapshot = Mockito.mock(DataTreeSnapshot.class);
266         Mockito.doThrow(new RuntimeException("mock ex")).when(mockSnapshot)
267                 .readNode(Mockito.any(YangInstanceIdentifier.class));
268
269         DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadTransaction("1", true, mockSnapshot);
270
271         doReadAndThrowEx(readTx);
272     }
273
274     @Test(expected = ReadFailedException.class)
275     public void testReadWithReadWriteTransactionClosed() throws Exception {
276
277         DOMStoreReadTransaction readTx = domStore.newReadWriteTransaction();
278         assertNotNull(readTx);
279
280         readTx.close();
281
282         doReadAndThrowEx(readTx);
283     }
284
285     @Test(expected = ReadFailedException.class)
286     public void testReadWithReadWriteTransactionFailure() throws Exception {
287
288         DataTreeSnapshot mockSnapshot = Mockito.mock(DataTreeSnapshot.class);
289         DataTreeModification mockModification = Mockito.mock(DataTreeModification.class);
290         Mockito.doThrow(new RuntimeException("mock ex")).when(mockModification)
291                 .readNode(Mockito.any(YangInstanceIdentifier.class));
292         Mockito.doReturn(mockModification).when(mockSnapshot).newModification();
293         @SuppressWarnings("unchecked")
294         TransactionReadyPrototype<String> mockReady = Mockito.mock(TransactionReadyPrototype.class);
295         DOMStoreReadTransaction readTx = SnapshotBackedTransactions.newReadWriteTransaction("1", false, mockSnapshot,
296                 mockReady);
297
298         doReadAndThrowEx(readTx);
299     }
300
301     private static void doReadAndThrowEx(final DOMStoreReadTransaction readTx) throws ReadFailedException  {
302         readTx.read(TestModel.TEST_PATH).checkedGet();
303     }
304
305     @Test(expected = IllegalStateException.class)
306     public void testWriteWithTransactionReady() {
307
308         DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
309
310         writeTx.ready();
311
312         // Should throw ex
313         writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
314     }
315
316     @Test(expected = IllegalStateException.class)
317     public void testReadyWithTransactionAlreadyReady() {
318
319         DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
320
321         writeTx.ready();
322
323         // Should throw ex
324         writeTx.ready();
325     }
326
327     @Test
328     public void testReadyWithMissingMandatoryData() throws InterruptedException {
329         DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
330         NormalizedNode<?, ?> testNode = ImmutableContainerNodeBuilder.create()
331                 .withNodeIdentifier(new NodeIdentifier(TestModel.MANDATORY_DATA_TEST_QNAME))
332                 .addChild(ImmutableNodes.leafNode(TestModel.OPTIONAL_QNAME, "data"))
333                 .build();
334         writeTx.write(TestModel.MANDATORY_DATA_TEST_PATH, testNode);
335         DOMStoreThreePhaseCommitCohort ready = writeTx.ready();
336         try {
337             ready.canCommit().get();
338             Assert.fail("Expected exception on canCommit");
339         } catch (ExecutionException e) {
340             // nop
341         }
342     }
343
344     @Test
345     public void testTransactionAbort() throws InterruptedException, ExecutionException {
346
347         DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
348         assertNotNull(writeTx);
349
350         assertTestContainerWrite(writeTx);
351
352         DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
353
354         assertTrue(cohort.canCommit().get().booleanValue());
355         cohort.preCommit().get();
356         cohort.abort().get();
357
358         Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)
359                 .get();
360         assertFalse(afterCommitRead.isPresent());
361     }
362
363     @Test
364     public void testTransactionChain() throws InterruptedException, ExecutionException {
365         DOMStoreTransactionChain txChain = domStore.createTransactionChain();
366         assertNotNull(txChain);
367
368         /*
369          * We alocate new read-write transaction and write /test
370          */
371         DOMStoreReadWriteTransaction firstTx = txChain.newReadWriteTransaction();
372         assertTestContainerWrite(firstTx);
373
374         /*
375          * First transaction is marked as ready, we are able to allocate chained
376          * transactions
377          */
378         final DOMStoreThreePhaseCommitCohort firstWriteTxCohort = firstTx.ready();
379
380         /*
381          * We alocate chained transaction - read transaction, note first one is
382          * still not commited to datastore.
383          */
384         DOMStoreReadTransaction secondReadTx = txChain.newReadOnlyTransaction();
385
386         /*
387          * We test if we are able to read data from tx, read should not fail
388          * since we are using chained transaction.
389          */
390         assertTestContainerExists(secondReadTx);
391
392         /*
393          * We alocate next transaction, which is still based on first one, but
394          * is read-write.
395          */
396         DOMStoreReadWriteTransaction thirdDeleteTx = txChain.newReadWriteTransaction();
397
398         /*
399          * We test existence of /test in third transaction container should
400          * still be visible from first one (which is still uncommmited).
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         assertThreePhaseCommit(firstWriteTxCohort);
418
419         // Alocates store transacion
420         DOMStoreReadTransaction storeReadTx = domStore.newReadOnlyTransaction();
421
422         /*
423          * We verify transaction is commited to store, container should exists
424          * in datastore.
425          */
426         assertTestContainerExists(storeReadTx);
427
428         /*
429          * We commit third transaction
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          * Writes /test in writeTx
464          */
465         writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
466
467         return assertTestContainerExists(writeTx);
468     }
469
470     /**
471      * Reads /test from readTx Read should return container.
472      */
473     private static Optional<NormalizedNode<?, ?>> assertTestContainerExists(final DOMStoreReadTransaction readTx)
474             throws InterruptedException, ExecutionException {
475
476         ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = readTx.read(TestModel.TEST_PATH);
477         assertTrue(writeTxContainer.get().isPresent());
478         return writeTxContainer.get();
479     }
480 }