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.MockRaftActorContext;
10 import org.opendaylight.controller.cluster.raft.RaftActorContext;
11 import org.opendaylight.controller.cluster.raft.RaftState;
12 import org.opendaylight.controller.cluster.raft.base.messages.ElectionTimeout;
13 import org.opendaylight.controller.cluster.raft.messages.AppendEntries;
14 import org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply;
15 import org.opendaylight.controller.cluster.raft.messages.RequestVote;
16 import org.opendaylight.controller.cluster.raft.messages.RequestVoteReply;
17 import org.opendaylight.controller.cluster.raft.utils.DoNothingActor;
19 import java.util.Collections;
20 import java.util.HashMap;
23 import static org.junit.Assert.assertEquals;
25 public class CandidateTest extends AbstractRaftActorBehaviorTest {
27 private final ActorRef candidateActor = getSystem().actorOf(Props.create(
28 DoNothingActor.class));
30 private final ActorRef peerActor1 = getSystem().actorOf(Props.create(
31 DoNothingActor.class));
33 private final ActorRef peerActor2 = getSystem().actorOf(Props.create(
34 DoNothingActor.class));
36 private final ActorRef peerActor3 = getSystem().actorOf(Props.create(
37 DoNothingActor.class));
39 private final ActorRef peerActor4 = getSystem().actorOf(Props.create(
40 DoNothingActor.class));
42 private final Map<String, String> onePeer = new HashMap<>();
43 private final Map<String, String> twoPeers = new HashMap<>();
44 private final Map<String, String> fourPeers = new HashMap<>();
48 onePeer.put(peerActor1.path().toString(),
49 peerActor1.path().toString());
51 twoPeers.put(peerActor1.path().toString(),
52 peerActor1.path().toString());
53 twoPeers.put(peerActor2.path().toString(),
54 peerActor2.path().toString());
56 fourPeers.put(peerActor1.path().toString(),
57 peerActor1.path().toString());
58 fourPeers.put(peerActor2.path().toString(),
59 peerActor2.path().toString());
60 fourPeers.put(peerActor3.path().toString(),
61 peerActor3.path().toString());
62 fourPeers.put(peerActor4.path().toString(),
63 peerActor3.path().toString());
69 public void testWhenACandidateIsCreatedItIncrementsTheCurrentTermAndVotesForItself(){
70 RaftActorContext raftActorContext = createActorContext();
71 long expectedTerm = raftActorContext.getTermInformation().getCurrentTerm();
73 new Candidate(raftActorContext);
75 assertEquals(expectedTerm+1, raftActorContext.getTermInformation().getCurrentTerm());
76 assertEquals(raftActorContext.getId(), raftActorContext.getTermInformation().getVotedFor());
80 public void testThatAnElectionTimeoutIsTriggered(){
81 new JavaTestKit(getSystem()) {{
83 new Within(duration("1 seconds")) {
84 protected void run() {
86 Candidate candidate = new Candidate(createActorContext(getTestActor()));
88 final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "ElectionTimeout") {
89 // do not put code outside this method, will run afterwards
90 protected Boolean match(Object in) {
91 if (in instanceof ElectionTimeout) {
99 assertEquals(true, out);
106 public void testHandleElectionTimeoutWhenThereAreZeroPeers(){
107 RaftActorContext raftActorContext = createActorContext();
108 Candidate candidate =
109 new Candidate(raftActorContext);
111 RaftState raftState =
112 candidate.handleMessage(candidateActor, new ElectionTimeout());
114 Assert.assertEquals(RaftState.Leader, raftState);
118 public void testHandleElectionTimeoutWhenThereAreTwoNodesInCluster(){
119 MockRaftActorContext raftActorContext =
120 (MockRaftActorContext) createActorContext();
121 raftActorContext.setPeerAddresses(onePeer);
122 Candidate candidate =
123 new Candidate(raftActorContext);
125 RaftState raftState =
126 candidate.handleMessage(candidateActor, new ElectionTimeout());
128 Assert.assertEquals(RaftState.Candidate, raftState);
132 public void testBecomeLeaderOnReceivingMajorityVotesInThreeNodesInCluster(){
133 MockRaftActorContext raftActorContext =
134 (MockRaftActorContext) createActorContext();
135 raftActorContext.setPeerAddresses(twoPeers);
136 Candidate candidate =
137 new Candidate(raftActorContext);
139 RaftState stateOnFirstVote = candidate.handleMessage(peerActor1, new RequestVoteReply(0, true));
141 Assert.assertEquals(RaftState.Leader, stateOnFirstVote);
146 public void testBecomeLeaderOnReceivingMajorityVotesInFiveNodesInCluster(){
147 MockRaftActorContext raftActorContext =
148 (MockRaftActorContext) createActorContext();
149 raftActorContext.setPeerAddresses(fourPeers);
150 Candidate candidate =
151 new Candidate(raftActorContext);
153 RaftState stateOnFirstVote = candidate.handleMessage(peerActor1, new RequestVoteReply(0, true));
155 RaftState stateOnSecondVote = candidate.handleMessage(peerActor2, new RequestVoteReply(0, true));
157 Assert.assertEquals(RaftState.Candidate, stateOnFirstVote);
158 Assert.assertEquals(RaftState.Leader, stateOnSecondVote);
163 public void testResponseToAppendEntriesWithLowerTerm(){
164 new JavaTestKit(getSystem()) {{
166 new Within(duration("1 seconds")) {
167 protected void run() {
169 Candidate candidate = new Candidate(createActorContext(getTestActor()));
171 candidate.handleMessage(getTestActor(), new AppendEntries(0, "test", 0,0,Collections.EMPTY_LIST, 0));
173 final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "AppendEntriesResponse") {
174 // do not put code outside this method, will run afterwards
175 protected Boolean match(Object in) {
176 if (in instanceof AppendEntriesReply) {
177 AppendEntriesReply reply = (AppendEntriesReply) in;
178 return reply.isSuccess();
185 assertEquals(false, out);
192 public void testResponseToRequestVoteWithLowerTerm(){
193 new JavaTestKit(getSystem()) {{
195 new Within(duration("1 seconds")) {
196 protected void run() {
198 Candidate candidate = new Candidate(createActorContext(getTestActor()));
200 candidate.handleMessage(getTestActor(), new RequestVote(0, "test", 0, 0));
202 final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "AppendEntriesResponse") {
203 // do not put code outside this method, will run afterwards
204 protected Boolean match(Object in) {
205 if (in instanceof RequestVoteReply) {
206 RequestVoteReply reply = (RequestVoteReply) in;
207 return reply.isVoteGranted();
214 assertEquals(false, out);
221 public void testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForIsNull(){
222 new JavaTestKit(getSystem()) {{
224 new Within(duration("1 seconds")) {
225 protected void run() {
227 RaftActorContext context = createActorContext(getTestActor());
229 context.getTermInformation().update(1000, null);
231 // Once a candidate is created it will immediately increment the current term so after
232 // construction the currentTerm should be 1001
233 RaftActorBehavior follower = createBehavior(context);
235 follower.handleMessage(getTestActor(), new RequestVote(1001, "test", 10000, 999));
237 final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "RequestVoteReply") {
238 // do not put code outside this method, will run afterwards
239 protected Boolean match(Object in) {
240 if (in instanceof RequestVoteReply) {
241 RequestVoteReply reply = (RequestVoteReply) in;
242 return reply.isVoteGranted();
249 assertEquals(true, out);
256 public void testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForIsNotTheSameAsCandidateId(){
257 new JavaTestKit(getSystem()) {{
259 new Within(duration("1 seconds")) {
260 protected void run() {
262 RaftActorContext context = createActorContext(getTestActor());
264 context.getTermInformation().update(1000, "test");
266 RaftActorBehavior follower = createBehavior(context);
268 follower.handleMessage(getTestActor(), new RequestVote(1001, "candidate", 10000, 999));
270 final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "RequestVoteReply") {
271 // do not put code outside this method, will run afterwards
272 protected Boolean match(Object in) {
273 if (in instanceof RequestVoteReply) {
274 RequestVoteReply reply = (RequestVoteReply) in;
275 return reply.isVoteGranted();
282 assertEquals(false, out);
290 @Override protected RaftActorBehavior createBehavior(RaftActorContext actorContext) {
291 return new Candidate(actorContext);
294 @Override protected RaftActorContext createActorContext() {
295 return new MockRaftActorContext("test", getSystem(), candidateActor);