Bug 1392: Change ReadTransaction#read to return CheckedFuture
[controller.git] / opendaylight / md-sal / sal-inmemory-datastore / src / test / java / org / opendaylight / controller / md / sal / dom / store / impl / InMemoryDataStoreTest.java
index 96369dea5fabc7d3ce25272f5e0cceaf5291a0c6..9b105aa3064121b518295220a5fb3351d030c13b 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.controller.md.sal.dom.store.impl;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
@@ -16,12 +17,22 @@ import java.util.concurrent.ExecutionException;
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Test;
+import org.mockito.Mockito;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.dom.store.impl.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
 import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 import com.google.common.base.Optional;
@@ -31,226 +42,373 @@ import com.google.common.util.concurrent.MoreExecutors;
 
 public class InMemoryDataStoreTest {
 
-  private SchemaContext schemaContext;
-  private InMemoryDOMDataStore domStore;
+    private SchemaContext schemaContext;
+    private InMemoryDOMDataStore domStore;
 
-  @Before
-  public void setupStore() {
-    domStore = new InMemoryDOMDataStore("TEST", MoreExecutors.sameThreadExecutor());
-    schemaContext = TestModel.createTestContext();
-    domStore.onGlobalContextUpdated(schemaContext);
+    @Before
+    public void setupStore() {
+        domStore = new InMemoryDOMDataStore("TEST", MoreExecutors.sameThreadExecutor());
+        schemaContext = TestModel.createTestContext();
+        domStore.onGlobalContextUpdated(schemaContext);
+    }
 
-  }
+    @Test
+    public void testTransactionIsolation() throws InterruptedException, ExecutionException {
 
-  @Test
-  public void testTransactionIsolation() throws InterruptedException, ExecutionException {
+        assertNotNull(domStore);
 
-    assertNotNull(domStore);
+        DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
+        assertNotNull(readTx);
 
-    DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
-    assertNotNull(readTx);
+        DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
+        assertNotNull(writeTx);
 
-    DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
-    assertNotNull(writeTx);
-    /**
-     *
-     * Writes /test in writeTx
-     *
-     */
-    writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        /**
+         * Writes /test in writeTx
+         */
+        NormalizedNode<?, ?> testNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
+        writeTx.write(TestModel.TEST_PATH, testNode);
 
-    /**
-     *
-     * Reads /test from writeTx Read should return container.
-     *
-     */
-    ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(TestModel.TEST_PATH);
-    assertTrue(writeTxContainer.get().isPresent());
+        /**
+         * Reads /test from writeTx Read should return container.
+         */
+        ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(TestModel.TEST_PATH);
+        assertEquals("read: isPresent", true, writeTxContainer.get().isPresent());
+        assertEquals("read: data", testNode, writeTxContainer.get().get());
 
-    /**
-     *
-     * Reads /test from readTx Read should return Absent.
-     *
-     */
-    ListenableFuture<Optional<NormalizedNode<?, ?>>> readTxContainer = readTx.read(TestModel.TEST_PATH);
-    assertFalse(readTxContainer.get().isPresent());
-  }
+        /**
+         * Reads /test from readTx Read should return Absent.
+         */
+        ListenableFuture<Optional<NormalizedNode<?, ?>>> readTxContainer = readTx.read(TestModel.TEST_PATH);
+        assertEquals("read: isPresent", false, readTxContainer.get().isPresent());
+    }
 
-  @Test
-  public void testTransactionCommit() throws InterruptedException, ExecutionException {
+    @Test
+    public void testTransactionCommit() throws InterruptedException, ExecutionException {
 
-    DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
-    assertNotNull(writeTx);
-    /**
-     *
-     * Writes /test in writeTx
-     *
-     */
-    writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
+        assertNotNull(writeTx);
 
-    /**
-     *
-     * Reads /test from writeTx Read should return container.
-     *
-     */
-    ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(TestModel.TEST_PATH);
-    assertTrue(writeTxContainer.get().isPresent());
+        /**
+         * Writes /test in writeTx
+         */
+        NormalizedNode<?, ?> testNode = ImmutableNodes.containerNode(TestModel.TEST_QNAME);
+        writeTx.write(TestModel.TEST_PATH, testNode);
 
-    DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
+        /**
+         * Reads /test from writeTx Read should return container.
+         */
+        ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = writeTx.read(TestModel.TEST_PATH);
+        assertEquals("read: isPresent", true, writeTxContainer.get().isPresent());
+        assertEquals("read: data", testNode, writeTxContainer.get().get());
 
-    assertThreePhaseCommit(cohort);
+        DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
 
-    Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)
-        .get();
-    assertTrue(afterCommitRead.isPresent());
-  }
+        assertThreePhaseCommit(cohort);
 
-  @Test
-  public void testTransactionAbort() throws InterruptedException, ExecutionException {
+        Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)
+                .get();
+        assertEquals("After commit read: isPresent", true, afterCommitRead.isPresent());
+        assertEquals("After commit read: data", testNode, afterCommitRead.get());
+    }
 
-    DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
-    assertNotNull(writeTx);
+    @Test
+    public void testDelete() throws Exception {
 
-    assertTestContainerWrite(writeTx);
+        DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
+        assertNotNull( writeTx );
 
-    DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
+        // Write /test and commit
 
-    assertTrue(cohort.canCommit().get().booleanValue());
-    cohort.preCommit().get();
-    cohort.abort().get();
+        writeTx.write( TestModel.TEST_PATH, ImmutableNodes.containerNode( TestModel.TEST_QNAME ) );
 
-    Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)
-        .get();
-    assertFalse(afterCommitRead.isPresent());
-  }
+        assertThreePhaseCommit( writeTx.ready() );
 
-  @Test
-  public void testTransactionChain() throws InterruptedException, ExecutionException {
-    DOMStoreTransactionChain txChain = domStore.createTransactionChain();
-    assertNotNull(txChain);
+        Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().
+                read(TestModel.TEST_PATH ).get();
+        assertEquals( "After commit read: isPresent", true, afterCommitRead.isPresent() );
 
-    /**
-     * We alocate new read-write transaction and write /test
-     *
-     *
-     */
-    DOMStoreReadWriteTransaction firstTx = txChain.newReadWriteTransaction();
-    assertTestContainerWrite(firstTx);
+        // Delete /test and verify
 
-    /**
-     * First transaction is marked as ready, we are able to allocate chained
-     * transactions
-     */
-    DOMStoreThreePhaseCommitCohort firstWriteTxCohort = firstTx.ready();
+        writeTx = domStore.newWriteOnlyTransaction();
 
-    /**
-     * We alocate chained transaction - read transaction, note first one is
-     * still not commited to datastore.
-     */
-    DOMStoreReadTransaction secondReadTx = txChain.newReadOnlyTransaction();
+        writeTx.delete( TestModel.TEST_PATH );
 
-    /**
-     *
-     * We test if we are able to read data from tx, read should not fail
-     * since we are using chained transaction.
-     *
-     *
-     */
-    assertTestContainerExists(secondReadTx);
+        assertThreePhaseCommit( writeTx.ready() );
 
-    /**
-     *
-     * We alocate next transaction, which is still based on first one, but
-     * is read-write.
-     *
-     */
-    DOMStoreReadWriteTransaction thirdDeleteTx = txChain.newReadWriteTransaction();
+        afterCommitRead = domStore.newReadOnlyTransaction().
+                read(TestModel.TEST_PATH ).get();
+        assertEquals( "After commit read: isPresent", false, afterCommitRead.isPresent() );
+    }
 
-    /**
-     * We test existence of /test in third transaction container should
-     * still be visible from first one (which is still uncommmited).
-     *
-     *
-     */
-    assertTestContainerExists(thirdDeleteTx);
+    @Test
+    public void testMerge() throws Exception {
 
-    /**
-     * We delete node in third transaction
-     */
-    thirdDeleteTx.delete(TestModel.TEST_PATH);
+        DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
+        assertNotNull( writeTx );
 
-    /**
-     * third transaction is sealed.
-     */
-    DOMStoreThreePhaseCommitCohort thirdDeleteTxCohort = thirdDeleteTx.ready();
+        ContainerNode containerNode = ImmutableContainerNodeBuilder.create()
+                .withNodeIdentifier( new NodeIdentifier( TestModel.TEST_QNAME ) )
+                .addChild( ImmutableNodes.mapNodeBuilder( TestModel.OUTER_LIST_QNAME )
+                        .addChild( ImmutableNodes.mapEntry( TestModel.OUTER_LIST_QNAME,
+                                                            TestModel.ID_QNAME, 1 ) ).build() ).build();
 
-    /**
-     * We commit first transaction
-     *
-     */
-    assertThreePhaseCommit(firstWriteTxCohort);
+        writeTx.merge( TestModel.TEST_PATH, containerNode );
 
-    // Alocates store transacion
-    DOMStoreReadTransaction storeReadTx = domStore.newReadOnlyTransaction();
-    /**
-     * We verify transaction is commited to store, container should exists
-     * in datastore.
-     */
-    assertTestContainerExists(storeReadTx);
-    /**
-     * We commit third transaction
-     *
-     */
-    assertThreePhaseCommit(thirdDeleteTxCohort);
-  }
+        assertThreePhaseCommit( writeTx.ready() );
 
-  @Test
-  @Ignore
-  public void testTransactionConflict() throws InterruptedException, ExecutionException {
-    DOMStoreReadWriteTransaction txOne = domStore.newReadWriteTransaction();
-    DOMStoreReadWriteTransaction txTwo = domStore.newReadWriteTransaction();
-    assertTestContainerWrite(txOne);
-    assertTestContainerWrite(txTwo);
+        Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().
+                read(TestModel.TEST_PATH ).get();
+        assertEquals( "After commit read: isPresent", true, afterCommitRead.isPresent() );
+        assertEquals( "After commit read: data", containerNode, afterCommitRead.get() );
 
-    /**
-     * Commits transaction
-     */
-    assertThreePhaseCommit(txOne.ready());
+        // Merge a new list entry node
 
-    /**
-     * Asserts that txTwo could not be commited
-     */
-    assertFalse(txTwo.ready().canCommit().get());
-  }
-
-  private static void assertThreePhaseCommit(final DOMStoreThreePhaseCommitCohort cohort)
-      throws InterruptedException, ExecutionException {
-    assertTrue(cohort.canCommit().get().booleanValue());
-    cohort.preCommit().get();
-    cohort.commit().get();
-  }
-
-  private static Optional<NormalizedNode<?, ?>> assertTestContainerWrite(final DOMStoreReadWriteTransaction writeTx)
-      throws InterruptedException, ExecutionException {
-    /**
-     *
-     * Writes /test in writeTx
-     *
-     */
-    writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        writeTx = domStore.newWriteOnlyTransaction();
+        assertNotNull( writeTx );
+
+        containerNode = ImmutableContainerNodeBuilder.create()
+                .withNodeIdentifier( new NodeIdentifier( TestModel.TEST_QNAME ) )
+                .addChild( ImmutableNodes.mapNodeBuilder( TestModel.OUTER_LIST_QNAME )
+                        .addChild( ImmutableNodes.mapEntry( TestModel.OUTER_LIST_QNAME,
+                                                            TestModel.ID_QNAME, 1 ) )
+                        .addChild( ImmutableNodes.mapEntry( TestModel.OUTER_LIST_QNAME,
+                                                            TestModel.ID_QNAME, 2 ) ).build() ).build();
+
+        writeTx.merge( TestModel.TEST_PATH, containerNode );
+
+        assertThreePhaseCommit( writeTx.ready() );
+
+        afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH ).get();
+        assertEquals( "After commit read: isPresent", true, afterCommitRead.isPresent() );
+        assertEquals( "After commit read: data", containerNode, afterCommitRead.get() );
+    }
+
+    @Test(expected=ReadFailedException.class)
+    public void testReadWithReadOnlyTransactionClosed() throws Throwable {
+
+        DOMStoreReadTransaction readTx = domStore.newReadOnlyTransaction();
+        assertNotNull( readTx );
+
+        readTx.close();
+
+        doReadAndThrowEx( readTx );
+    }
+
+    @Test(expected=ReadFailedException.class)
+    public void testReadWithReadOnlyTransactionFailure() throws Throwable {
+
+        DataTreeSnapshot mockSnapshot = Mockito.mock( DataTreeSnapshot.class );
+        Mockito.doThrow( new RuntimeException( "mock ex" ) ).when( mockSnapshot )
+        .readNode( Mockito.any( YangInstanceIdentifier.class ) );
+
+        DOMStoreReadTransaction readTx = new SnapshotBackedReadTransaction( "1", mockSnapshot );
+
+        doReadAndThrowEx( readTx );
+    }
 
-    return assertTestContainerExists(writeTx);
-  }
+    @Test(expected=ReadFailedException.class)
+    public void testReadWithReadWriteTransactionClosed() throws Throwable {
 
-  /**
-   * Reads /test from readTx Read should return container.
-   */
-  private static Optional<NormalizedNode<?, ?>> assertTestContainerExists(final DOMStoreReadTransaction readTx)
-      throws InterruptedException, ExecutionException {
+        DOMStoreReadTransaction readTx = domStore.newReadWriteTransaction();
+        assertNotNull( readTx );
+
+        readTx.close();
+
+        doReadAndThrowEx( readTx );
+    }
+
+    @Test(expected=ReadFailedException.class)
+    public void testReadWithReadWriteTransactionFailure() throws Throwable {
+
+        DataTreeSnapshot mockSnapshot = Mockito.mock( DataTreeSnapshot.class );
+        DataTreeModification mockModification = Mockito.mock( DataTreeModification.class );
+        Mockito.doThrow( new RuntimeException( "mock ex" ) ).when( mockModification )
+        .readNode( Mockito.any( YangInstanceIdentifier.class ) );
+        Mockito.doReturn( mockModification ).when( mockSnapshot ).newModification();
+        TransactionReadyPrototype mockReady = Mockito.mock( TransactionReadyPrototype.class );
+        DOMStoreReadTransaction readTx = new SnapshotBackedReadWriteTransaction( "1", mockSnapshot, mockReady );
+
+        doReadAndThrowEx( readTx );
+    }
+
+    private void doReadAndThrowEx( DOMStoreReadTransaction readTx ) throws Throwable {
+
+        try {
+            readTx.read(TestModel.TEST_PATH).get();
+        } catch( ExecutionException e ) {
+            throw e.getCause();
+        }
+    }
+
+    @Test(expected=IllegalStateException.class)
+    public void testWriteWithTransactionReady() throws Exception {
+
+        DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
+
+        writeTx.ready();
+
+        // Should throw ex
+        writeTx.write( TestModel.TEST_PATH, ImmutableNodes.containerNode( TestModel.TEST_QNAME ) );
+    }
+
+    @Test(expected=IllegalStateException.class)
+    public void testReadyWithTransactionAlreadyReady() throws Exception {
+
+        DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
+
+        writeTx.ready();
+
+        // Should throw ex
+        writeTx.ready();
+    }
+
+    @Test
+    public void testTransactionAbort() throws InterruptedException, ExecutionException {
+
+        DOMStoreReadWriteTransaction writeTx = domStore.newReadWriteTransaction();
+        assertNotNull(writeTx);
+
+        assertTestContainerWrite(writeTx);
+
+        DOMStoreThreePhaseCommitCohort cohort = writeTx.ready();
+
+        assertTrue(cohort.canCommit().get().booleanValue());
+        cohort.preCommit().get();
+        cohort.abort().get();
+
+        Optional<NormalizedNode<?, ?>> afterCommitRead = domStore.newReadOnlyTransaction().read(TestModel.TEST_PATH)
+                .get();
+        assertFalse(afterCommitRead.isPresent());
+    }
+
+    @Test
+    public void testTransactionChain() throws InterruptedException, ExecutionException {
+        DOMStoreTransactionChain txChain = domStore.createTransactionChain();
+        assertNotNull(txChain);
+
+        /**
+         * We alocate new read-write transaction and write /test
+         *
+         *
+         */
+        DOMStoreReadWriteTransaction firstTx = txChain.newReadWriteTransaction();
+        assertTestContainerWrite(firstTx);
+
+        /**
+         * First transaction is marked as ready, we are able to allocate chained
+         * transactions
+         */
+        DOMStoreThreePhaseCommitCohort firstWriteTxCohort = firstTx.ready();
+
+        /**
+         * We alocate chained transaction - read transaction, note first one is
+         * still not commited to datastore.
+         */
+        DOMStoreReadTransaction secondReadTx = txChain.newReadOnlyTransaction();
+
+        /**
+         *
+         * We test if we are able to read data from tx, read should not fail
+         * since we are using chained transaction.
+         *
+         *
+         */
+        assertTestContainerExists(secondReadTx);
+
+        /**
+         *
+         * We alocate next transaction, which is still based on first one, but
+         * is read-write.
+         *
+         */
+        DOMStoreReadWriteTransaction thirdDeleteTx = txChain.newReadWriteTransaction();
+
+        /**
+         * We test existence of /test in third transaction container should
+         * still be visible from first one (which is still uncommmited).
+         *
+         *
+         */
+        assertTestContainerExists(thirdDeleteTx);
+
+        /**
+         * We delete node in third transaction
+         */
+        thirdDeleteTx.delete(TestModel.TEST_PATH);
+
+        /**
+         * third transaction is sealed.
+         */
+        DOMStoreThreePhaseCommitCohort thirdDeleteTxCohort = thirdDeleteTx.ready();
+
+        /**
+         * We commit first transaction
+         *
+         */
+        assertThreePhaseCommit(firstWriteTxCohort);
+
+        // Alocates store transacion
+        DOMStoreReadTransaction storeReadTx = domStore.newReadOnlyTransaction();
+        /**
+         * We verify transaction is commited to store, container should exists
+         * in datastore.
+         */
+        assertTestContainerExists(storeReadTx);
+        /**
+         * We commit third transaction
+         *
+         */
+        assertThreePhaseCommit(thirdDeleteTxCohort);
+    }
+
+    @Test
+    @Ignore
+    public void testTransactionConflict() throws InterruptedException, ExecutionException {
+        DOMStoreReadWriteTransaction txOne = domStore.newReadWriteTransaction();
+        DOMStoreReadWriteTransaction txTwo = domStore.newReadWriteTransaction();
+        assertTestContainerWrite(txOne);
+        assertTestContainerWrite(txTwo);
+
+        /**
+         * Commits transaction
+         */
+        assertThreePhaseCommit(txOne.ready());
+
+        /**
+         * Asserts that txTwo could not be commited
+         */
+        assertFalse(txTwo.ready().canCommit().get());
+    }
+
+    private static void assertThreePhaseCommit(final DOMStoreThreePhaseCommitCohort cohort)
+            throws InterruptedException, ExecutionException {
+        assertTrue(cohort.canCommit().get().booleanValue());
+        cohort.preCommit().get();
+        cohort.commit().get();
+    }
+
+    private static Optional<NormalizedNode<?, ?>> assertTestContainerWrite(final DOMStoreReadWriteTransaction writeTx)
+            throws InterruptedException, ExecutionException {
+        /**
+         *
+         * Writes /test in writeTx
+         *
+         */
+        writeTx.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+
+        return assertTestContainerExists(writeTx);
+    }
+
+    /**
+     * Reads /test from readTx Read should return container.
+     */
+    private static Optional<NormalizedNode<?, ?>> assertTestContainerExists(final DOMStoreReadTransaction readTx)
+            throws InterruptedException, ExecutionException {
 
-    ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = readTx.read(TestModel.TEST_PATH);
-    assertTrue(writeTxContainer.get().isPresent());
-    return writeTxContainer.get();
-  }
+        ListenableFuture<Optional<NormalizedNode<?, ?>>> writeTxContainer = readTx.read(TestModel.TEST_PATH);
+        assertTrue(writeTxContainer.get().isPresent());
+        return writeTxContainer.get();
+    }
 
 }