1 package org.opendaylight.controller.cluster.raft.behaviors;
3 import akka.actor.ActorRef;
4 import akka.actor.Props;
5 import akka.testkit.JavaTestKit;
6 import junit.framework.Assert;
7 import org.junit.Before;
9 import org.opendaylight.controller.cluster.raft.DefaultConfigParamsImpl;
10 import org.opendaylight.controller.cluster.raft.MockRaftActorContext;
11 import org.opendaylight.controller.cluster.raft.RaftActorContext;
12 import org.opendaylight.controller.cluster.raft.RaftState;
13 import org.opendaylight.controller.cluster.raft.base.messages.ElectionTimeout;
14 import org.opendaylight.controller.cluster.raft.messages.AppendEntries;
15 import org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply;
16 import org.opendaylight.controller.cluster.raft.messages.RequestVote;
17 import org.opendaylight.controller.cluster.raft.messages.RequestVoteReply;
18 import org.opendaylight.controller.cluster.raft.utils.DoNothingActor;
20 import java.util.Collections;
21 import java.util.HashMap;
24 import static org.junit.Assert.assertEquals;
26 public class CandidateTest extends AbstractRaftActorBehaviorTest {
28 private final ActorRef candidateActor = getSystem().actorOf(Props.create(
29 DoNothingActor.class));
31 private final ActorRef peerActor1 = getSystem().actorOf(Props.create(
32 DoNothingActor.class));
34 private final ActorRef peerActor2 = getSystem().actorOf(Props.create(
35 DoNothingActor.class));
37 private final ActorRef peerActor3 = getSystem().actorOf(Props.create(
38 DoNothingActor.class));
40 private final ActorRef peerActor4 = getSystem().actorOf(Props.create(
41 DoNothingActor.class));
43 private final Map<String, String> onePeer = new HashMap<>();
44 private final Map<String, String> twoPeers = new HashMap<>();
45 private final Map<String, String> fourPeers = new HashMap<>();
49 onePeer.put(peerActor1.path().toString(),
50 peerActor1.path().toString());
52 twoPeers.put(peerActor1.path().toString(),
53 peerActor1.path().toString());
54 twoPeers.put(peerActor2.path().toString(),
55 peerActor2.path().toString());
57 fourPeers.put(peerActor1.path().toString(),
58 peerActor1.path().toString());
59 fourPeers.put(peerActor2.path().toString(),
60 peerActor2.path().toString());
61 fourPeers.put(peerActor3.path().toString(),
62 peerActor3.path().toString());
63 fourPeers.put(peerActor4.path().toString(),
64 peerActor3.path().toString());
70 public void testWhenACandidateIsCreatedItIncrementsTheCurrentTermAndVotesForItself(){
71 RaftActorContext raftActorContext = createActorContext();
72 long expectedTerm = raftActorContext.getTermInformation().getCurrentTerm();
74 new Candidate(raftActorContext);
76 assertEquals(expectedTerm+1, raftActorContext.getTermInformation().getCurrentTerm());
77 assertEquals(raftActorContext.getId(), raftActorContext.getTermInformation().getVotedFor());
81 public void testThatAnElectionTimeoutIsTriggered(){
82 new JavaTestKit(getSystem()) {{
84 new Within(DefaultConfigParamsImpl.HEART_BEAT_INTERVAL.$times(6)) {
85 protected void run() {
87 Candidate candidate = new Candidate(createActorContext(getTestActor()));
89 final Boolean out = new ExpectMsg<Boolean>(DefaultConfigParamsImpl.HEART_BEAT_INTERVAL.$times(6), "ElectionTimeout") {
90 // do not put code outside this method, will run afterwards
91 protected Boolean match(Object in) {
92 if (in instanceof ElectionTimeout) {
100 assertEquals(true, out);
107 public void testHandleElectionTimeoutWhenThereAreZeroPeers(){
108 RaftActorContext raftActorContext = createActorContext();
109 Candidate candidate =
110 new Candidate(raftActorContext);
112 RaftState raftState =
113 candidate.handleMessage(candidateActor, new ElectionTimeout());
115 Assert.assertEquals(RaftState.Leader, raftState);
119 public void testHandleElectionTimeoutWhenThereAreTwoNodesInCluster(){
120 MockRaftActorContext raftActorContext =
121 (MockRaftActorContext) createActorContext();
122 raftActorContext.setPeerAddresses(onePeer);
123 Candidate candidate =
124 new Candidate(raftActorContext);
126 RaftState raftState =
127 candidate.handleMessage(candidateActor, new ElectionTimeout());
129 Assert.assertEquals(RaftState.Candidate, raftState);
133 public void testBecomeLeaderOnReceivingMajorityVotesInThreeNodesInCluster(){
134 MockRaftActorContext raftActorContext =
135 (MockRaftActorContext) createActorContext();
136 raftActorContext.setPeerAddresses(twoPeers);
137 Candidate candidate =
138 new Candidate(raftActorContext);
140 RaftState stateOnFirstVote = candidate.handleMessage(peerActor1, new RequestVoteReply(0, true));
142 Assert.assertEquals(RaftState.Leader, stateOnFirstVote);
147 public void testBecomeLeaderOnReceivingMajorityVotesInFiveNodesInCluster(){
148 MockRaftActorContext raftActorContext =
149 (MockRaftActorContext) createActorContext();
150 raftActorContext.setPeerAddresses(fourPeers);
151 Candidate candidate =
152 new Candidate(raftActorContext);
154 RaftState stateOnFirstVote = candidate.handleMessage(peerActor1, new RequestVoteReply(0, true));
156 RaftState stateOnSecondVote = candidate.handleMessage(peerActor2, new RequestVoteReply(0, true));
158 Assert.assertEquals(RaftState.Candidate, stateOnFirstVote);
159 Assert.assertEquals(RaftState.Leader, stateOnSecondVote);
164 public void testResponseToAppendEntriesWithLowerTerm(){
165 new JavaTestKit(getSystem()) {{
167 new Within(duration("1 seconds")) {
168 protected void run() {
170 Candidate candidate = new Candidate(createActorContext(getTestActor()));
172 candidate.handleMessage(getTestActor(), new AppendEntries(0, "test", 0,0,Collections.EMPTY_LIST, 0));
174 final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "AppendEntriesResponse") {
175 // do not put code outside this method, will run afterwards
176 protected Boolean match(Object in) {
177 if (in instanceof AppendEntriesReply) {
178 AppendEntriesReply reply = (AppendEntriesReply) in;
179 return reply.isSuccess();
186 assertEquals(false, out);
193 public void testResponseToRequestVoteWithLowerTerm(){
194 new JavaTestKit(getSystem()) {{
196 new Within(duration("1 seconds")) {
197 protected void run() {
199 Candidate candidate = new Candidate(createActorContext(getTestActor()));
201 candidate.handleMessage(getTestActor(), new RequestVote(0, "test", 0, 0));
203 final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "AppendEntriesResponse") {
204 // do not put code outside this method, will run afterwards
205 protected Boolean match(Object in) {
206 if (in instanceof RequestVoteReply) {
207 RequestVoteReply reply = (RequestVoteReply) in;
208 return reply.isVoteGranted();
215 assertEquals(false, out);
222 public void testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForIsNull(){
223 new JavaTestKit(getSystem()) {{
225 new Within(duration("1 seconds")) {
226 protected void run() {
228 RaftActorContext context = createActorContext(getTestActor());
230 context.getTermInformation().update(1000, null);
232 // Once a candidate is created it will immediately increment the current term so after
233 // construction the currentTerm should be 1001
234 RaftActorBehavior follower = createBehavior(context);
236 follower.handleMessage(getTestActor(), new RequestVote(1001, "test", 10000, 999));
238 final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "RequestVoteReply") {
239 // do not put code outside this method, will run afterwards
240 protected Boolean match(Object in) {
241 if (in instanceof RequestVoteReply) {
242 RequestVoteReply reply = (RequestVoteReply) in;
243 return reply.isVoteGranted();
250 assertEquals(true, out);
257 public void testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForIsNotTheSameAsCandidateId(){
258 new JavaTestKit(getSystem()) {{
260 new Within(duration("1 seconds")) {
261 protected void run() {
263 RaftActorContext context = createActorContext(getTestActor());
265 context.getTermInformation().update(1000, "test");
267 RaftActorBehavior follower = createBehavior(context);
269 follower.handleMessage(getTestActor(), new RequestVote(1001, "candidate", 10000, 999));
271 final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "RequestVoteReply") {
272 // do not put code outside this method, will run afterwards
273 protected Boolean match(Object in) {
274 if (in instanceof RequestVoteReply) {
275 RequestVoteReply reply = (RequestVoteReply) in;
276 return reply.isVoteGranted();
283 assertEquals(false, out);
291 @Override protected RaftActorBehavior createBehavior(RaftActorContext actorContext) {
292 return new Candidate(actorContext);
295 @Override protected RaftActorContext createActorContext() {
296 return new MockRaftActorContext("test", getSystem(), candidateActor);