1 package org.opendaylight.controller.cluster.raft.behaviors;
3 import static org.junit.Assert.assertEquals;
4 import akka.actor.ActorRef;
5 import akka.actor.Props;
6 import akka.testkit.JavaTestKit;
7 import java.util.Collections;
8 import java.util.HashMap;
10 import org.junit.Assert;
11 import org.junit.Before;
12 import org.junit.Test;
13 import org.opendaylight.controller.cluster.raft.DefaultConfigParamsImpl;
14 import org.opendaylight.controller.cluster.raft.MockRaftActorContext;
15 import org.opendaylight.controller.cluster.raft.RaftActorContext;
16 import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry;
17 import org.opendaylight.controller.cluster.raft.base.messages.ElectionTimeout;
18 import org.opendaylight.controller.cluster.raft.messages.AppendEntries;
19 import org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply;
20 import org.opendaylight.controller.cluster.raft.messages.RequestVote;
21 import org.opendaylight.controller.cluster.raft.messages.RequestVoteReply;
22 import org.opendaylight.controller.cluster.raft.utils.DoNothingActor;
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)) {
84 protected void run() {
86 Candidate candidate = new Candidate(createActorContext(getTestActor()));
88 final Boolean out = new ExpectMsg<Boolean>(DefaultConfigParamsImpl.HEART_BEAT_INTERVAL.$times(6), "ElectionTimeout") {
89 // 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 RaftActorBehavior raftBehavior =
113 candidate.handleMessage(candidateActor, new ElectionTimeout());
115 Assert.assertTrue(raftBehavior instanceof Leader);
119 public void testHandleElectionTimeoutWhenThereAreTwoNodesInCluster(){
120 MockRaftActorContext raftActorContext =
121 createActorContext();
122 raftActorContext.setPeerAddresses(onePeer);
123 Candidate candidate =
124 new Candidate(raftActorContext);
126 RaftActorBehavior raftBehavior =
127 candidate.handleMessage(candidateActor, new ElectionTimeout());
129 Assert.assertTrue(raftBehavior instanceof Candidate);
133 public void testBecomeLeaderOnReceivingMajorityVotesInThreeNodesInCluster(){
134 MockRaftActorContext raftActorContext =
135 createActorContext();
136 raftActorContext.setPeerAddresses(twoPeers);
137 Candidate candidate =
138 new Candidate(raftActorContext);
140 RaftActorBehavior behaviorOnFirstVote = candidate.handleMessage(peerActor1, new RequestVoteReply(0, true));
142 Assert.assertTrue(behaviorOnFirstVote instanceof Leader);
147 public void testBecomeLeaderOnReceivingMajorityVotesInFiveNodesInCluster(){
148 MockRaftActorContext raftActorContext =
149 createActorContext();
150 raftActorContext.setPeerAddresses(fourPeers);
151 Candidate candidate =
152 new Candidate(raftActorContext);
154 RaftActorBehavior behaviorOnFirstVote = candidate.handleMessage(peerActor1, new RequestVoteReply(0, true));
156 RaftActorBehavior behaviorOnSecondVote = candidate.handleMessage(peerActor2, new RequestVoteReply(0, true));
158 Assert.assertTrue(behaviorOnFirstVote instanceof Candidate);
159 Assert.assertTrue(behaviorOnSecondVote instanceof Leader);
164 public void testResponseToAppendEntriesWithLowerTerm(){
165 new JavaTestKit(getSystem()) {{
167 new Within(duration("1 seconds")) {
169 protected void run() {
171 Candidate candidate = new Candidate(createActorContext(getTestActor()));
173 candidate.handleMessage(getTestActor(), new AppendEntries(0, "test", 0,0,Collections.<ReplicatedLogEntry>emptyList(), 0, -1));
175 final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "AppendEntriesResponse") {
176 // do not put code outside this method, will run afterwards
178 protected Boolean match(Object in) {
179 if (in instanceof AppendEntriesReply) {
180 AppendEntriesReply reply = (AppendEntriesReply) in;
181 return reply.isSuccess();
188 assertEquals(false, out);
195 public void testResponseToRequestVoteWithLowerTerm(){
196 new JavaTestKit(getSystem()) {{
198 new Within(duration("1 seconds")) {
200 protected void run() {
202 Candidate candidate = new Candidate(createActorContext(getTestActor()));
204 candidate.handleMessage(getTestActor(), new RequestVote(0, "test", 0, 0));
206 final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "AppendEntriesResponse") {
207 // do not put code outside this method, will run afterwards
209 protected Boolean match(Object in) {
210 if (in instanceof RequestVoteReply) {
211 RequestVoteReply reply = (RequestVoteReply) in;
212 return reply.isVoteGranted();
219 assertEquals(false, out);
226 public void testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForIsNull(){
227 new JavaTestKit(getSystem()) {{
229 new Within(duration("1 seconds")) {
231 protected void run() {
233 RaftActorContext context = createActorContext(getTestActor());
235 context.getTermInformation().update(1000, null);
237 // Once a candidate is created it will immediately increment the current term so after
238 // construction the currentTerm should be 1001
239 RaftActorBehavior follower = createBehavior(context);
241 follower.handleMessage(getTestActor(), new RequestVote(1001, "test", 10000, 999));
243 final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "RequestVoteReply") {
244 // do not put code outside this method, will run afterwards
246 protected Boolean match(Object in) {
247 if (in instanceof RequestVoteReply) {
248 RequestVoteReply reply = (RequestVoteReply) in;
249 return reply.isVoteGranted();
256 assertEquals(true, out);
263 public void testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForIsNotTheSameAsCandidateId(){
264 new JavaTestKit(getSystem()) {{
266 new Within(duration("1 seconds")) {
268 protected void run() {
270 RaftActorContext context = createActorContext(getTestActor());
272 context.getTermInformation().update(1000, "test");
274 RaftActorBehavior follower = createBehavior(context);
276 follower.handleMessage(getTestActor(), new RequestVote(1001, "candidate", 10000, 999));
278 final Boolean out = new ExpectMsg<Boolean>(duration("1 seconds"), "RequestVoteReply") {
279 // do not put code outside this method, will run afterwards
281 protected Boolean match(Object in) {
282 if (in instanceof RequestVoteReply) {
283 RequestVoteReply reply = (RequestVoteReply) in;
284 return reply.isVoteGranted();
291 assertEquals(false, out);
299 @Override protected RaftActorBehavior createBehavior(RaftActorContext actorContext) {
300 return new Candidate(actorContext);
303 @Override protected MockRaftActorContext createActorContext() {
304 return new MockRaftActorContext("test", getSystem(), candidateActor);