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 org.junit.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.ReplicatedLogEntry;
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;
19 import java.util.Collections;
20 import java.util.HashMap;
22 import static org.junit.Assert.assertEquals;
24 public class CandidateTest extends AbstractRaftActorBehaviorTest {
26 private final ActorRef candidateActor = getSystem().actorOf(Props.create(
27 DoNothingActor.class));
29 private final ActorRef peerActor1 = getSystem().actorOf(Props.create(
30 DoNothingActor.class));
32 private final ActorRef peerActor2 = getSystem().actorOf(Props.create(
33 DoNothingActor.class));
35 private final ActorRef peerActor3 = getSystem().actorOf(Props.create(
36 DoNothingActor.class));
38 private final ActorRef peerActor4 = getSystem().actorOf(Props.create(
39 DoNothingActor.class));
41 private final Map<String, String> onePeer = new HashMap<>();
42 private final Map<String, String> twoPeers = new HashMap<>();
43 private final Map<String, String> fourPeers = new HashMap<>();
47 onePeer.put(peerActor1.path().toString(),
48 peerActor1.path().toString());
50 twoPeers.put(peerActor1.path().toString(),
51 peerActor1.path().toString());
52 twoPeers.put(peerActor2.path().toString(),
53 peerActor2.path().toString());
55 fourPeers.put(peerActor1.path().toString(),
56 peerActor1.path().toString());
57 fourPeers.put(peerActor2.path().toString(),
58 peerActor2.path().toString());
59 fourPeers.put(peerActor3.path().toString(),
60 peerActor3.path().toString());
61 fourPeers.put(peerActor4.path().toString(),
62 peerActor3.path().toString());
68 public void testWhenACandidateIsCreatedItIncrementsTheCurrentTermAndVotesForItself(){
69 RaftActorContext raftActorContext = createActorContext();
70 long expectedTerm = raftActorContext.getTermInformation().getCurrentTerm();
72 new Candidate(raftActorContext);
74 assertEquals(expectedTerm+1, raftActorContext.getTermInformation().getCurrentTerm());
75 assertEquals(raftActorContext.getId(), raftActorContext.getTermInformation().getVotedFor());
79 public void testThatAnElectionTimeoutIsTriggered(){
80 new JavaTestKit(getSystem()) {{
82 new Within(DefaultConfigParamsImpl.HEART_BEAT_INTERVAL.$times(6)) {
83 protected void run() {
85 Candidate candidate = new Candidate(createActorContext(getTestActor()));
87 final Boolean out = new ExpectMsg<Boolean>(DefaultConfigParamsImpl.HEART_BEAT_INTERVAL.$times(6), "ElectionTimeout") {
88 // do not put code outside this method, will run afterwards
89 protected Boolean match(Object in) {
90 if (in instanceof ElectionTimeout) {
98 assertEquals(true, out);
105 public void testHandleElectionTimeoutWhenThereAreZeroPeers(){
106 RaftActorContext raftActorContext = createActorContext();
107 Candidate candidate =
108 new Candidate(raftActorContext);
110 RaftActorBehavior raftBehavior =
111 candidate.handleMessage(candidateActor, new ElectionTimeout());
113 Assert.assertTrue(raftBehavior instanceof Leader);
117 public void testHandleElectionTimeoutWhenThereAreTwoNodesInCluster(){
118 MockRaftActorContext raftActorContext =
119 (MockRaftActorContext) createActorContext();
120 raftActorContext.setPeerAddresses(onePeer);
121 Candidate candidate =
122 new Candidate(raftActorContext);
124 RaftActorBehavior raftBehavior =
125 candidate.handleMessage(candidateActor, new ElectionTimeout());
127 Assert.assertTrue(raftBehavior instanceof Candidate);
131 public void testBecomeLeaderOnReceivingMajorityVotesInThreeNodesInCluster(){
132 MockRaftActorContext raftActorContext =
133 (MockRaftActorContext) createActorContext();
134 raftActorContext.setPeerAddresses(twoPeers);
135 Candidate candidate =
136 new Candidate(raftActorContext);
138 RaftActorBehavior behaviorOnFirstVote = candidate.handleMessage(peerActor1, new RequestVoteReply(0, true));
140 Assert.assertTrue(behaviorOnFirstVote instanceof Leader);
145 public void testBecomeLeaderOnReceivingMajorityVotesInFiveNodesInCluster(){
146 MockRaftActorContext raftActorContext =
147 (MockRaftActorContext) createActorContext();
148 raftActorContext.setPeerAddresses(fourPeers);
149 Candidate candidate =
150 new Candidate(raftActorContext);
152 RaftActorBehavior behaviorOnFirstVote = candidate.handleMessage(peerActor1, new RequestVoteReply(0, true));
154 RaftActorBehavior behaviorOnSecondVote = candidate.handleMessage(peerActor2, new RequestVoteReply(0, true));
156 Assert.assertTrue(behaviorOnFirstVote instanceof Candidate);
157 Assert.assertTrue(behaviorOnSecondVote instanceof Leader);
162 public void testResponseToAppendEntriesWithLowerTerm(){
163 new JavaTestKit(getSystem()) {{
165 new Within(duration("1 seconds")) {
166 protected void run() {
168 Candidate candidate = new Candidate(createActorContext(getTestActor()));
170 candidate.handleMessage(getTestActor(), new AppendEntries(0, "test", 0,0,Collections.<ReplicatedLogEntry>emptyList(), 0));
172 final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "AppendEntriesResponse") {
173 // do not put code outside this method, will run afterwards
174 protected Boolean match(Object in) {
175 if (in instanceof AppendEntriesReply) {
176 AppendEntriesReply reply = (AppendEntriesReply) in;
177 return reply.isSuccess();
184 assertEquals(false, out);
191 public void testResponseToRequestVoteWithLowerTerm(){
192 new JavaTestKit(getSystem()) {{
194 new Within(duration("1 seconds")) {
195 protected void run() {
197 Candidate candidate = new Candidate(createActorContext(getTestActor()));
199 candidate.handleMessage(getTestActor(), new RequestVote(0, "test", 0, 0));
201 final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "AppendEntriesResponse") {
202 // do not put code outside this method, will run afterwards
203 protected Boolean match(Object in) {
204 if (in instanceof RequestVoteReply) {
205 RequestVoteReply reply = (RequestVoteReply) in;
206 return reply.isVoteGranted();
213 assertEquals(false, out);
220 public void testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForIsNull(){
221 new JavaTestKit(getSystem()) {{
223 new Within(duration("1 seconds")) {
224 protected void run() {
226 RaftActorContext context = createActorContext(getTestActor());
228 context.getTermInformation().update(1000, null);
230 // Once a candidate is created it will immediately increment the current term so after
231 // construction the currentTerm should be 1001
232 RaftActorBehavior follower = createBehavior(context);
234 follower.handleMessage(getTestActor(), new RequestVote(1001, "test", 10000, 999));
236 final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "RequestVoteReply") {
237 // do not put code outside this method, will run afterwards
238 protected Boolean match(Object in) {
239 if (in instanceof RequestVoteReply) {
240 RequestVoteReply reply = (RequestVoteReply) in;
241 return reply.isVoteGranted();
248 assertEquals(true, out);
255 public void testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForIsNotTheSameAsCandidateId(){
256 new JavaTestKit(getSystem()) {{
258 new Within(duration("1 seconds")) {
259 protected void run() {
261 RaftActorContext context = createActorContext(getTestActor());
263 context.getTermInformation().update(1000, "test");
265 RaftActorBehavior follower = createBehavior(context);
267 follower.handleMessage(getTestActor(), new RequestVote(1001, "candidate", 10000, 999));
269 final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "RequestVoteReply") {
270 // do not put code outside this method, will run afterwards
271 protected Boolean match(Object in) {
272 if (in instanceof RequestVoteReply) {
273 RequestVoteReply reply = (RequestVoteReply) in;
274 return reply.isVoteGranted();
281 assertEquals(false, out);
289 @Override protected RaftActorBehavior createBehavior(RaftActorContext actorContext) {
290 return new Candidate(actorContext);
293 @Override protected RaftActorContext createActorContext() {
294 return new MockRaftActorContext("test", getSystem(), candidateActor);