Fix license header violations in sal-akka-raft
[controller.git] / opendaylight / md-sal / sal-akka-raft / src / test / java / org / opendaylight / controller / cluster / raft / behaviors / CandidateTest.java
1 /*
2  * Copyright (c) 2014, 2015 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
9 package org.opendaylight.controller.cluster.raft.behaviors;
10
11 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertTrue;
13 import akka.actor.ActorRef;
14 import akka.actor.Props;
15 import akka.testkit.TestActorRef;
16 import com.google.common.base.Stopwatch;
17 import java.util.Collections;
18 import java.util.HashMap;
19 import java.util.Map;
20 import java.util.concurrent.TimeUnit;
21 import org.junit.After;
22 import org.junit.Before;
23 import org.junit.Test;
24 import org.opendaylight.controller.cluster.raft.MockRaftActorContext;
25 import org.opendaylight.controller.cluster.raft.RaftActorContext;
26 import org.opendaylight.controller.cluster.raft.RaftState;
27 import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry;
28 import org.opendaylight.controller.cluster.raft.base.messages.ElectionTimeout;
29 import org.opendaylight.controller.cluster.raft.messages.AppendEntries;
30 import org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply;
31 import org.opendaylight.controller.cluster.raft.messages.RaftRPC;
32 import org.opendaylight.controller.cluster.raft.messages.RequestVote;
33 import org.opendaylight.controller.cluster.raft.messages.RequestVoteReply;
34 import org.opendaylight.controller.cluster.raft.utils.MessageCollectorActor;
35
36 public class CandidateTest extends AbstractRaftActorBehaviorTest {
37
38     private final TestActorRef<MessageCollectorActor> candidateActor = actorFactory.createTestActor(
39             Props.create(MessageCollectorActor.class), actorFactory.generateActorId("candidate"));
40
41     private TestActorRef<MessageCollectorActor>[] peerActors;
42
43     private RaftActorBehavior candidate;
44
45     @Before
46     public void setUp(){
47     }
48
49     @Override
50     @After
51     public void tearDown() throws Exception {
52         if(candidate != null) {
53             candidate.close();
54         }
55
56         super.tearDown();
57     }
58
59     @Test
60     public void testWhenACandidateIsCreatedItIncrementsTheCurrentTermAndVotesForItself(){
61         RaftActorContext raftActorContext = createActorContext();
62         long expectedTerm = raftActorContext.getTermInformation().getCurrentTerm();
63
64         candidate = new Candidate(raftActorContext);
65
66         assertEquals("getCurrentTerm", expectedTerm+1, raftActorContext.getTermInformation().getCurrentTerm());
67         assertEquals("getVotedFor", raftActorContext.getId(), raftActorContext.getTermInformation().getVotedFor());
68     }
69
70     @Test
71     public void testThatAnElectionTimeoutIsTriggered(){
72          MockRaftActorContext actorContext = createActorContext();
73          candidate = new Candidate(actorContext);
74
75          MessageCollectorActor.expectFirstMatching(candidateActor, ElectionTimeout.class,
76                  actorContext.getConfigParams().getElectionTimeOutInterval().$times(6).toMillis());
77     }
78
79     @Test
80     public void testHandleElectionTimeoutWhenThereAreZeroPeers(){
81         RaftActorContext raftActorContext = createActorContext();
82         candidate = new Candidate(raftActorContext);
83
84         RaftActorBehavior newBehavior =
85             candidate.handleMessage(candidateActor, new ElectionTimeout());
86
87         assertEquals("Behavior", RaftState.Leader, newBehavior.state());
88     }
89
90     @Test
91     public void testHandleElectionTimeoutWhenThereAreTwoNodeCluster(){
92         MockRaftActorContext raftActorContext = createActorContext();
93         raftActorContext.setPeerAddresses(setupPeers(1));
94         candidate = new Candidate(raftActorContext);
95
96         candidate = candidate.handleMessage(candidateActor, new ElectionTimeout());
97
98         assertEquals("Behavior", RaftState.Candidate, candidate.state());
99     }
100
101     @Test
102     public void testBecomeLeaderOnReceivingMajorityVotesInThreeNodeCluster(){
103         MockRaftActorContext raftActorContext = createActorContext();
104         raftActorContext.setPeerAddresses(setupPeers(2));
105         candidate = new Candidate(raftActorContext);
106
107         candidate = candidate.handleMessage(peerActors[0], new RequestVoteReply(1, true));
108
109         assertEquals("Behavior", RaftState.Leader, candidate.state());
110     }
111
112     @Test
113     public void testBecomeLeaderOnReceivingMajorityVotesInFiveNodeCluster(){
114         MockRaftActorContext raftActorContext = createActorContext();
115         raftActorContext.setPeerAddresses(setupPeers(4));
116         candidate = new Candidate(raftActorContext);
117
118         // First peers denies the vote.
119         candidate = candidate.handleMessage(peerActors[0], new RequestVoteReply(1, false));
120
121         assertEquals("Behavior", RaftState.Candidate, candidate.state());
122
123         candidate = candidate.handleMessage(peerActors[1], new RequestVoteReply(1, true));
124
125         assertEquals("Behavior", RaftState.Candidate, candidate.state());
126
127         candidate = candidate.handleMessage(peerActors[2], new RequestVoteReply(1, true));
128
129         assertEquals("Behavior", RaftState.Leader, candidate.state());
130     }
131
132     @Test
133     public void testResponseToHandleAppendEntriesWithLowerTerm() {
134         candidate = new Candidate(createActorContext());
135
136         setupPeers(1);
137         candidate.handleMessage(peerActors[0], new AppendEntries(1, "test", 0, 0,
138                 Collections.<ReplicatedLogEntry>emptyList(), 0, -1, (short)0));
139
140         AppendEntriesReply reply = MessageCollectorActor.expectFirstMatching(
141                 peerActors[0], AppendEntriesReply.class);
142         assertEquals("isSuccess", false, reply.isSuccess());
143         assertEquals("getTerm", 2, reply.getTerm());
144     }
145
146     @Test
147     public void testResponseToRequestVoteWithLowerTerm() {
148         candidate = new Candidate(createActorContext());
149
150         setupPeers(1);
151         candidate.handleMessage(peerActors[0], new RequestVote(1, "test", 0, 0));
152
153         RequestVoteReply reply = MessageCollectorActor.expectFirstMatching(
154                 peerActors[0], RequestVoteReply.class);
155         assertEquals("isVoteGranted", false, reply.isVoteGranted());
156         assertEquals("getTerm", 2, reply.getTerm());
157     }
158
159     @Test
160     public void testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForMatches() {
161         MockRaftActorContext context = createActorContext();
162         context.getTermInformation().update(1000, null);
163
164         // Once a candidate is created it will immediately increment the current term so after
165         // construction the currentTerm should be 1001
166         candidate = new Candidate(context);
167
168         setupPeers(1);
169         candidate.handleMessage(peerActors[0], new RequestVote(1001, context.getId(), 10000, 999));
170
171         RequestVoteReply reply = MessageCollectorActor.expectFirstMatching(
172                 peerActors[0], RequestVoteReply.class);
173         assertEquals("isVoteGranted", true, reply.isVoteGranted());
174         assertEquals("getTerm", 1001, reply.getTerm());
175     }
176
177     @Test
178     public void testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForDoesNotMatch() {
179         MockRaftActorContext context = createActorContext();
180         context.getTermInformation().update(1000, null);
181
182         // Once a candidate is created it will immediately increment the current term so after
183         // construction the currentTerm should be 1001
184         candidate = new Candidate(context);
185
186         setupPeers(1);
187
188         // RequestVote candidate ID ("candidate2") does not match this candidate's votedFor
189         // (it votes for itself)
190         candidate.handleMessage(peerActors[0], new RequestVote(1001, "candidate2", 10000, 999));
191
192         RequestVoteReply reply = MessageCollectorActor.expectFirstMatching(
193                 peerActors[0], RequestVoteReply.class);
194         assertEquals("isVoteGranted", false, reply.isVoteGranted());
195         assertEquals("getTerm", 1001, reply.getTerm());
196     }
197
198     @Test
199     public void testCandidateSchedulesElectionTimeoutImmediatelyWhenItHasNoPeers(){
200         MockRaftActorContext context = createActorContext();
201
202         Stopwatch stopwatch = Stopwatch.createStarted();
203
204         candidate = createBehavior(context);
205
206         MessageCollectorActor.expectFirstMatching(candidateActor, ElectionTimeout.class);
207
208         long elapsed = stopwatch.elapsed(TimeUnit.MILLISECONDS);
209
210         assertTrue(elapsed < context.getConfigParams().getElectionTimeOutInterval().toMillis());
211     }
212
213
214     @Override
215     protected RaftActorBehavior createBehavior(RaftActorContext actorContext) {
216         return new Candidate(actorContext);
217     }
218
219     @Override protected MockRaftActorContext createActorContext() {
220         return new MockRaftActorContext("candidate", getSystem(), candidateActor);
221     }
222
223     private Map<String, String> setupPeers(int count) {
224         Map<String, String> peerMap = new HashMap<>();
225         peerActors = new TestActorRef[count];
226         for(int i = 0; i < count; i++) {
227             peerActors[i] = actorFactory.createTestActor(Props.create(MessageCollectorActor.class),
228                     actorFactory.generateActorId("peer"));
229             peerMap.put("peer" + (i+1), peerActors[i].path().toString());
230         }
231
232         return peerMap;
233     }
234
235     @Override
236     protected void assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(RaftActorContext actorContext,
237             ActorRef actorRef, RaftRPC rpc) throws Exception {
238         super.assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(actorContext, actorRef, rpc);
239         assertEquals("New votedFor", null, actorContext.getTermInformation().getVotedFor());
240     }
241 }