X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=blobdiff_plain;f=opendaylight%2Fmd-sal%2Fsal-akka-raft%2Fsrc%2Ftest%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fcluster%2Fraft%2Fbehaviors%2FCandidateTest.java;h=60f45523cf204d5d246fb14ac01eddc4dcc760ba;hp=9efdbd7c54a497d6b07ce11977f35b7814a58e99;hb=ea3673e89598b896c93ebee864e6cb8db7f6c6ec;hpb=97222f19035815199200e727f43960513073eb9e diff --git a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/CandidateTest.java b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/CandidateTest.java index 9efdbd7c54..60f45523cf 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/CandidateTest.java +++ b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/CandidateTest.java @@ -1,207 +1,216 @@ package org.opendaylight.controller.cluster.raft.behaviors; +import static org.junit.Assert.assertEquals; import akka.actor.ActorRef; import akka.actor.Props; -import akka.testkit.JavaTestKit; -import junit.framework.Assert; +import akka.testkit.TestActorRef; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import org.junit.After; +import org.junit.Before; import org.junit.Test; import org.opendaylight.controller.cluster.raft.MockRaftActorContext; import org.opendaylight.controller.cluster.raft.RaftActorContext; import org.opendaylight.controller.cluster.raft.RaftState; -import org.opendaylight.controller.cluster.raft.internal.messages.ElectionTimeout; +import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry; +import org.opendaylight.controller.cluster.raft.base.messages.ElectionTimeout; import org.opendaylight.controller.cluster.raft.messages.AppendEntries; import org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply; +import org.opendaylight.controller.cluster.raft.messages.RaftRPC; import org.opendaylight.controller.cluster.raft.messages.RequestVote; import org.opendaylight.controller.cluster.raft.messages.RequestVoteReply; -import org.opendaylight.controller.cluster.raft.utils.DoNothingActor; - -import java.util.Arrays; -import java.util.Collections; - -import static org.junit.Assert.assertEquals; +import org.opendaylight.controller.cluster.raft.utils.MessageCollectorActor; public class CandidateTest extends AbstractRaftActorBehaviorTest { - private final ActorRef candidateActor = getSystem().actorOf(Props.create( - DoNothingActor.class)); + private final TestActorRef candidateActor = actorFactory.createTestActor( + Props.create(MessageCollectorActor.class), actorFactory.generateActorId("candidate")); + + private TestActorRef[] peerActors; - private final ActorRef peerActor1 = getSystem().actorOf(Props.create( - DoNothingActor.class)); + private RaftActorBehavior candidate; - private final ActorRef peerActor2 = getSystem().actorOf(Props.create( - DoNothingActor.class)); + @Before + public void setUp(){ + } - private final ActorRef peerActor3 = getSystem().actorOf(Props.create( - DoNothingActor.class)); + @Override + @After + public void tearDown() throws Exception { + if(candidate != null) { + candidate.close(); + } - private final ActorRef peerActor4 = getSystem().actorOf(Props.create( - DoNothingActor.class)); + super.tearDown(); + } @Test public void testWhenACandidateIsCreatedItIncrementsTheCurrentTermAndVotesForItself(){ RaftActorContext raftActorContext = createActorContext(); - long expectedTerm = raftActorContext.getTermInformation().getCurrentTerm().get(); + long expectedTerm = raftActorContext.getTermInformation().getCurrentTerm(); - new Candidate(raftActorContext, Collections.EMPTY_LIST); + candidate = new Candidate(raftActorContext); - assertEquals(expectedTerm+1, raftActorContext.getTermInformation().getCurrentTerm().get()); - assertEquals(raftActorContext.getId(), raftActorContext.getTermInformation().getVotedFor()); + assertEquals("getCurrentTerm", expectedTerm+1, raftActorContext.getTermInformation().getCurrentTerm()); + assertEquals("getVotedFor", raftActorContext.getId(), raftActorContext.getTermInformation().getVotedFor()); } @Test public void testThatAnElectionTimeoutIsTriggered(){ - new JavaTestKit(getSystem()) {{ - - new Within(duration("1 seconds")) { - protected void run() { - - Candidate candidate = new Candidate(createActorContext(getTestActor()), Collections.EMPTY_LIST); + MockRaftActorContext actorContext = createActorContext(); + candidate = new Candidate(actorContext); - final Boolean out = new ExpectMsg(duration("1 seconds"), "ElectionTimeout") { - // do not put code outside this method, will run afterwards - protected Boolean match(Object in) { - if (in instanceof ElectionTimeout) { - return true; - } else { - throw noMatch(); - } - } - }.get(); - - assertEquals(true, out); - } - }; - }}; + MessageCollectorActor.expectFirstMatching(candidateActor, ElectionTimeout.class, + actorContext.getConfigParams().getElectionTimeOutInterval().$times(6).toMillis()); } @Test public void testHandleElectionTimeoutWhenThereAreZeroPeers(){ RaftActorContext raftActorContext = createActorContext(); - Candidate candidate = - new Candidate(raftActorContext, Collections.EMPTY_LIST); + candidate = new Candidate(raftActorContext); - RaftState raftState = + RaftActorBehavior newBehavior = candidate.handleMessage(candidateActor, new ElectionTimeout()); - Assert.assertEquals(RaftState.Leader, raftState); + assertEquals("Behavior", RaftState.Leader, newBehavior.state()); } @Test - public void testHandleElectionTimeoutWhenThereAreTwoPeers(){ - RaftActorContext raftActorContext = createActorContext(); - Candidate candidate = - new Candidate(raftActorContext, Arrays - .asList(peerActor1.path().toString(), - peerActor2.path().toString())); + public void testHandleElectionTimeoutWhenThereAreTwoNodeCluster(){ + MockRaftActorContext raftActorContext = createActorContext(); + raftActorContext.setPeerAddresses(setupPeers(1)); + candidate = new Candidate(raftActorContext); - RaftState raftState = - candidate.handleMessage(candidateActor, new ElectionTimeout()); + candidate = candidate.handleMessage(candidateActor, new ElectionTimeout()); - Assert.assertEquals(RaftState.Candidate, raftState); + assertEquals("Behavior", RaftState.Candidate, candidate.state()); } @Test - public void testBecomeLeaderOnReceivingMajorityVotesInThreePeerCluster(){ - RaftActorContext raftActorContext = createActorContext(); - Candidate candidate = - new Candidate(raftActorContext, Arrays - .asList(peerActor1.path().toString(), - peerActor2.path().toString())); - - RaftState stateOnFirstVote = candidate.handleMessage(peerActor1, new RequestVoteReply(0, true)); + public void testBecomeLeaderOnReceivingMajorityVotesInThreeNodeCluster(){ + MockRaftActorContext raftActorContext = createActorContext(); + raftActorContext.setPeerAddresses(setupPeers(2)); + candidate = new Candidate(raftActorContext); - Assert.assertEquals(RaftState.Leader, stateOnFirstVote); + candidate = candidate.handleMessage(peerActors[0], new RequestVoteReply(1, true)); + assertEquals("Behavior", RaftState.Leader, candidate.state()); } @Test - public void testBecomeLeaderOnReceivingMajorityVotesInFivePeerCluster(){ - RaftActorContext raftActorContext = createActorContext(); - Candidate candidate = - new Candidate(raftActorContext, Arrays - .asList(peerActor1.path().toString(), - peerActor2.path().toString(), - peerActor3.path().toString())); + public void testBecomeLeaderOnReceivingMajorityVotesInFiveNodeCluster(){ + MockRaftActorContext raftActorContext = createActorContext(); + raftActorContext.setPeerAddresses(setupPeers(4)); + candidate = new Candidate(raftActorContext); + + // First peers denies the vote. + candidate = candidate.handleMessage(peerActors[0], new RequestVoteReply(1, false)); - RaftState stateOnFirstVote = candidate.handleMessage(peerActor1, new RequestVoteReply(0, true)); + assertEquals("Behavior", RaftState.Candidate, candidate.state()); - RaftState stateOnSecondVote = candidate.handleMessage(peerActor1, new RequestVoteReply(0, true)); + candidate = candidate.handleMessage(peerActors[1], new RequestVoteReply(1, true)); - Assert.assertEquals(RaftState.Candidate, stateOnFirstVote); - Assert.assertEquals(RaftState.Leader, stateOnSecondVote); + assertEquals("Behavior", RaftState.Candidate, candidate.state()); + candidate = candidate.handleMessage(peerActors[2], new RequestVoteReply(1, true)); + + assertEquals("Behavior", RaftState.Leader, candidate.state()); } @Test - public void testResponseToAppendEntriesWithLowerTerm(){ - new JavaTestKit(getSystem()) {{ + public void testResponseToHandleAppendEntriesWithLowerTerm() { + candidate = new Candidate(createActorContext()); - new Within(duration("1 seconds")) { - protected void run() { + setupPeers(1); + candidate.handleMessage(peerActors[0], new AppendEntries(1, "test", 0, 0, + Collections.emptyList(), 0, -1)); - Candidate candidate = new Candidate(createActorContext(getTestActor()), Collections.EMPTY_LIST); + AppendEntriesReply reply = MessageCollectorActor.expectFirstMatching( + peerActors[0], AppendEntriesReply.class); + assertEquals("isSuccess", false, reply.isSuccess()); + assertEquals("getTerm", 2, reply.getTerm()); + } - candidate.handleMessage(getTestActor(), new AppendEntries(0, "test", 0,0,Collections.EMPTY_LIST, 0)); + @Test + public void testResponseToRequestVoteWithLowerTerm() { + candidate = new Candidate(createActorContext()); - final Boolean out = new ExpectMsg(duration("1 seconds"), "AppendEntriesResponse") { - // do not put code outside this method, will run afterwards - protected Boolean match(Object in) { - if (in instanceof AppendEntriesReply) { - AppendEntriesReply reply = (AppendEntriesReply) in; - return reply.isSuccess(); - } else { - throw noMatch(); - } - } - }.get(); + setupPeers(1); + candidate.handleMessage(peerActors[0], new RequestVote(1, "test", 0, 0)); - assertEquals(false, out); - } - }; - }}; + RequestVoteReply reply = MessageCollectorActor.expectFirstMatching( + peerActors[0], RequestVoteReply.class); + assertEquals("isVoteGranted", false, reply.isVoteGranted()); + assertEquals("getTerm", 2, reply.getTerm()); } @Test - public void testResponseToRequestVoteWithLowerTerm(){ - new JavaTestKit(getSystem()) {{ + public void testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForMatches() { + MockRaftActorContext context = createActorContext(); + context.getTermInformation().update(1000, null); - new Within(duration("1 seconds")) { - protected void run() { + // Once a candidate is created it will immediately increment the current term so after + // construction the currentTerm should be 1001 + candidate = new Candidate(context); - Candidate candidate = new Candidate(createActorContext(getTestActor()), Collections.EMPTY_LIST); + setupPeers(1); + candidate.handleMessage(peerActors[0], new RequestVote(1001, context.getId(), 10000, 999)); - candidate.handleMessage(getTestActor(), new RequestVote(0, "test", 0, 0)); + RequestVoteReply reply = MessageCollectorActor.expectFirstMatching( + peerActors[0], RequestVoteReply.class); + assertEquals("isVoteGranted", true, reply.isVoteGranted()); + assertEquals("getTerm", 1001, reply.getTerm()); + } + + @Test + public void testHandleRequestVoteWhenSenderTermEqualToCurrentTermAndVotedForDoesNotMatch() { + MockRaftActorContext context = createActorContext(); + context.getTermInformation().update(1000, null); + + // Once a candidate is created it will immediately increment the current term so after + // construction the currentTerm should be 1001 + candidate = new Candidate(context); - final Boolean out = new ExpectMsg(duration("1 seconds"), "AppendEntriesResponse") { - // do not put code outside this method, will run afterwards - protected Boolean match(Object in) { - if (in instanceof RequestVoteReply) { - RequestVoteReply reply = (RequestVoteReply) in; - return reply.isVoteGranted(); - } else { - throw noMatch(); - } - } - }.get(); + setupPeers(1); - assertEquals(false, out); - } - }; - }}; + // RequestVote candidate ID ("candidate2") does not match this candidate's votedFor + // (it votes for itself) + candidate.handleMessage(peerActors[0], new RequestVote(1001, "candidate2", 10000, 999)); + + RequestVoteReply reply = MessageCollectorActor.expectFirstMatching( + peerActors[0], RequestVoteReply.class); + assertEquals("isVoteGranted", false, reply.isVoteGranted()); + assertEquals("getTerm", 1001, reply.getTerm()); } - @Override protected RaftActorBehavior createBehavior(RaftActorContext actorContext) { - return new Candidate(actorContext, Collections.EMPTY_LIST); + @Override + protected RaftActorBehavior createBehavior(RaftActorContext actorContext) { + return new Candidate(actorContext); } - @Override protected RaftActorContext createActorContext() { - return new MockRaftActorContext("test", getSystem(), candidateActor); + @Override protected MockRaftActorContext createActorContext() { + return new MockRaftActorContext("candidate", getSystem(), candidateActor); } - protected RaftActorContext createActorContext(ActorRef candidateActor) { - return new MockRaftActorContext("test", getSystem(), candidateActor); + private Map setupPeers(int count) { + Map peerMap = new HashMap<>(); + peerActors = new TestActorRef[count]; + for(int i = 0; i < count; i++) { + peerActors[i] = actorFactory.createTestActor(Props.create(MessageCollectorActor.class), + actorFactory.generateActorId("peer")); + peerMap.put("peer" + (i+1), peerActors[i].path().toString()); + } + + return peerMap; } + @Override + protected void assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(RaftActorContext actorContext, + ActorRef actorRef, RaftRPC rpc) throws Exception { + super.assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(actorContext, actorRef, rpc); + assertEquals("New votedFor", null, actorContext.getTermInformation().getVotedFor()); + } }