Add VotingFuture unit test
[controller.git] / opendaylight / md-sal / sal-distributed-datastore / src / test / java / org / opendaylight / controller / cluster / databroker / actors / dds / VotingFutureTest.java
1 /*
2  * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.cluster.databroker.actors.dds;
9
10 import java.util.concurrent.ExecutionException;
11 import java.util.concurrent.Executors;
12 import java.util.concurrent.Future;
13 import java.util.concurrent.ScheduledExecutorService;
14 import java.util.concurrent.TimeUnit;
15 import java.util.concurrent.atomic.AtomicBoolean;
16 import org.junit.After;
17 import org.junit.Assert;
18 import org.junit.Before;
19 import org.junit.Test;
20
21 public class VotingFutureTest {
22
23     private static final int TIMEOUT = 3;
24
25     private Object result;
26     private ScheduledExecutorService executor;
27     private VotingFuture<Object> future;
28
29     @Before
30     public void setUp() throws Exception {
31         result = new Object();
32         future = new VotingFuture<>(result, 3);
33         executor = Executors.newScheduledThreadPool(1);
34     }
35
36     @After
37     public void tearDown() throws Exception {
38         executor.shutdownNow();
39     }
40
41     @Test
42     public void testTrivialCases() throws Exception {
43         final VotingFuture<Object> oneYesVoteFuture = new VotingFuture<>(result, 1);
44         oneYesVoteFuture.voteYes();
45         checkSuccess(oneYesVoteFuture, result);
46         final VotingFuture<Object> oneNoVoteFuture = new VotingFuture<>(result, 1);
47         final RuntimeException cause = new RuntimeException("fail");
48         oneNoVoteFuture.voteNo(cause);
49         checkException(oneNoVoteFuture, cause);
50     }
51
52     @Test
53     public void testVoteYes() throws Exception {
54         future.voteYes();
55         future.voteYes();
56         future.voteYes();
57         checkSuccess(future, result);
58     }
59
60     @Test
61     public void testVoteYesBlocking() throws Exception {
62         final AtomicBoolean voted = new AtomicBoolean(false);
63         future.voteYes();
64         future.voteYes();
65         executor.schedule(() -> {
66             voted.set(true);
67             future.voteYes();
68         }, 1, TimeUnit.SECONDS);
69         checkSuccess(future, result);
70         Assert.assertTrue("Future completed before vote", voted.get());
71     }
72
73     @Test
74     public void testVoteNo() throws Exception {
75         future.voteYes();
76         final RuntimeException cause = new RuntimeException("fail");
77         future.voteNo(cause);
78         future.voteYes();
79         checkException(future, cause);
80     }
81
82     @Test
83     public void testVoteNoFirst() throws Exception {
84         final RuntimeException cause = new RuntimeException("fail");
85         future.voteNo(cause);
86         future.voteYes();
87         future.voteYes();
88         checkException(future, cause);
89     }
90
91     @Test
92     public void testVoteNoLast() throws Exception {
93         future.voteYes();
94         future.voteYes();
95         final RuntimeException cause = new RuntimeException("fail");
96         future.voteNo(cause);
97         checkException(future, cause);
98     }
99
100     @Test
101     public void testVoteNoBlocking() throws Exception {
102         final AtomicBoolean voted = new AtomicBoolean(false);
103         future.voteYes();
104         final RuntimeException cause = new RuntimeException("fail");
105         future.voteNo(cause);
106         executor.schedule(() -> {
107             voted.set(true);
108             future.voteYes();
109         }, 1, TimeUnit.SECONDS);
110         checkException(future, cause);
111         Assert.assertTrue("Future completed before vote", voted.get());
112     }
113
114     @Test
115     public void testMultipleVoteNo() throws Exception {
116         future.voteYes();
117         final RuntimeException cause1 = new RuntimeException("fail");
118         final RuntimeException cause2 = new RuntimeException("fail");
119         future.voteNo(cause1);
120         future.voteNo(cause2);
121         try {
122             future.get(TIMEOUT, TimeUnit.SECONDS);
123             Assert.fail("ExecutionException expected");
124         } catch (final ExecutionException e) {
125             //first no is set as cause
126             Assert.assertEquals(cause1, e.getCause());
127             //subsequent no causes are added as suppressed
128             final Throwable[] suppressed = e.getCause().getSuppressed();
129             Assert.assertEquals(1, suppressed.length);
130             Assert.assertEquals(cause2, suppressed[0]);
131         }
132     }
133
134     private static void checkException(final Future future, final RuntimeException cause) throws Exception {
135         try {
136             future.get(TIMEOUT, TimeUnit.SECONDS);
137             Assert.fail("ExecutionException expected");
138         } catch (final ExecutionException e) {
139             Assert.assertEquals(cause, e.getCause());
140         }
141     }
142
143     private static void checkSuccess(final Future future, final Object result) throws Exception {
144         Assert.assertEquals(result, future.get(TIMEOUT, TimeUnit.SECONDS));
145     }
146
147 }