Add VotingFuture unit test 81/53081/6
authorAndrej Mak <andrej.mak@pantheon.tech>
Thu, 9 Mar 2017 14:19:38 +0000 (15:19 +0100)
committerTom Pantelis <tpanteli@brocade.com>
Mon, 13 Mar 2017 12:52:46 +0000 (12:52 +0000)
Change-Id: Ib3abc7c59d1047d508e5d2446acc50ebf67e1b44
Signed-off-by: Andrej Mak <andrej.mak@pantheon.tech>
opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/databroker/actors/dds/VotingFutureTest.java [new file with mode: 0644]

diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/databroker/actors/dds/VotingFutureTest.java b/opendaylight/md-sal/sal-distributed-datastore/src/test/java/org/opendaylight/controller/cluster/databroker/actors/dds/VotingFutureTest.java
new file mode 100644 (file)
index 0000000..85d6582
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.databroker.actors.dds;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class VotingFutureTest {
+
+    private static final int TIMEOUT = 3;
+
+    private Object result;
+    private ScheduledExecutorService executor;
+    private VotingFuture<Object> future;
+
+    @Before
+    public void setUp() throws Exception {
+        result = new Object();
+        future = new VotingFuture<>(result, 3);
+        executor = Executors.newScheduledThreadPool(1);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        executor.shutdownNow();
+    }
+
+    @Test
+    public void testTrivialCases() throws Exception {
+        final VotingFuture<Object> oneYesVoteFuture = new VotingFuture<>(result, 1);
+        oneYesVoteFuture.voteYes();
+        checkSuccess(oneYesVoteFuture, result);
+        final VotingFuture<Object> oneNoVoteFuture = new VotingFuture<>(result, 1);
+        final RuntimeException cause = new RuntimeException("fail");
+        oneNoVoteFuture.voteNo(cause);
+        checkException(oneNoVoteFuture, cause);
+    }
+
+    @Test
+    public void testVoteYes() throws Exception {
+        future.voteYes();
+        future.voteYes();
+        future.voteYes();
+        checkSuccess(future, result);
+    }
+
+    @Test
+    public void testVoteYesBlocking() throws Exception {
+        final AtomicBoolean voted = new AtomicBoolean(false);
+        future.voteYes();
+        future.voteYes();
+        executor.schedule(() -> {
+            voted.set(true);
+            future.voteYes();
+        }, 1, TimeUnit.SECONDS);
+        checkSuccess(future, result);
+        Assert.assertTrue("Future completed before vote", voted.get());
+    }
+
+    @Test
+    public void testVoteNo() throws Exception {
+        future.voteYes();
+        final RuntimeException cause = new RuntimeException("fail");
+        future.voteNo(cause);
+        future.voteYes();
+        checkException(future, cause);
+    }
+
+    @Test
+    public void testVoteNoFirst() throws Exception {
+        final RuntimeException cause = new RuntimeException("fail");
+        future.voteNo(cause);
+        future.voteYes();
+        future.voteYes();
+        checkException(future, cause);
+    }
+
+    @Test
+    public void testVoteNoLast() throws Exception {
+        future.voteYes();
+        future.voteYes();
+        final RuntimeException cause = new RuntimeException("fail");
+        future.voteNo(cause);
+        checkException(future, cause);
+    }
+
+    @Test
+    public void testVoteNoBlocking() throws Exception {
+        final AtomicBoolean voted = new AtomicBoolean(false);
+        future.voteYes();
+        final RuntimeException cause = new RuntimeException("fail");
+        future.voteNo(cause);
+        executor.schedule(() -> {
+            voted.set(true);
+            future.voteYes();
+        }, 1, TimeUnit.SECONDS);
+        checkException(future, cause);
+        Assert.assertTrue("Future completed before vote", voted.get());
+    }
+
+    @Test
+    public void testMultipleVoteNo() throws Exception {
+        future.voteYes();
+        final RuntimeException cause1 = new RuntimeException("fail");
+        final RuntimeException cause2 = new RuntimeException("fail");
+        future.voteNo(cause1);
+        future.voteNo(cause2);
+        try {
+            future.get(TIMEOUT, TimeUnit.SECONDS);
+            Assert.fail("ExecutionException expected");
+        } catch (final ExecutionException e) {
+            //first no is set as cause
+            Assert.assertEquals(cause1, e.getCause());
+            //subsequent no causes are added as suppressed
+            final Throwable[] suppressed = e.getCause().getSuppressed();
+            Assert.assertEquals(1, suppressed.length);
+            Assert.assertEquals(cause2, suppressed[0]);
+        }
+    }
+
+    private static void checkException(final Future future, final RuntimeException cause) throws Exception {
+        try {
+            future.get(TIMEOUT, TimeUnit.SECONDS);
+            Assert.fail("ExecutionException expected");
+        } catch (final ExecutionException e) {
+            Assert.assertEquals(cause, e.getCause());
+        }
+    }
+
+    private static void checkSuccess(final Future future, final Object result) throws Exception {
+        Assert.assertEquals(result, future.get(TIMEOUT, TimeUnit.SECONDS));
+    }
+
+}
\ No newline at end of file