From: tpantelis Date: Wed, 18 Feb 2015 02:26:21 +0000 (-0500) Subject: Refactor AbstractRaftActorBehaviorTest and IsolatedLeaderTest X-Git-Tag: release/lithium~532 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=94374bc65cc6c57f1d2e77e1cd15902bb69d4fea Refactor AbstractRaftActorBehaviorTest and IsolatedLeaderTest Removed the Within blocks in tests. Used TestActorFactory to create and cleanup actors. Added some more assertions in some tests. Change-Id: Idd790b97dd396c75676843779ceab192b848aa17 Signed-off-by: tpantelis --- diff --git a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractRaftActorBehaviorTest.java b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractRaftActorBehaviorTest.java index 4cfcc3fb9e..f56755b447 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractRaftActorBehaviorTest.java +++ b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/AbstractRaftActorBehaviorTest.java @@ -1,10 +1,9 @@ package org.opendaylight.controller.cluster.raft.behaviors; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; import akka.actor.ActorRef; import akka.actor.Props; -import akka.testkit.JavaTestKit; +import akka.testkit.TestActorRef; import com.google.protobuf.ByteString; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -12,25 +11,42 @@ import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.List; import java.util.Map; +import org.junit.After; +import org.junit.Assert; import org.junit.Test; import org.opendaylight.controller.cluster.raft.AbstractActorTest; 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.ReplicatedLogEntry; import org.opendaylight.controller.cluster.raft.SerializationUtils; +import org.opendaylight.controller.cluster.raft.TestActorFactory; 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.protobuff.client.messages.Payload; -import org.opendaylight.controller.cluster.raft.utils.DoNothingActor; +import org.opendaylight.controller.cluster.raft.utils.MessageCollectorActor; import org.slf4j.LoggerFactory; public abstract class AbstractRaftActorBehaviorTest extends AbstractActorTest { - private final ActorRef behaviorActor = getSystem().actorOf(Props.create( - DoNothingActor.class)); + protected final TestActorFactory actorFactory = new TestActorFactory(getSystem()); + + private final TestActorRef behaviorActor = actorFactory.createTestActor( + Props.create(MessageCollectorActor.class), actorFactory.generateActorId("behavior")); + + RaftActorBehavior behavior; + + @After + public void tearDown() throws Exception { + if(behavior != null) { + behavior.close(); + } + + actorFactory.close(); + } /** * This test checks that when a new Raft RPC message is received with a newer @@ -40,22 +56,19 @@ public abstract class AbstractRaftActorBehaviorTest extends AbstractActorTest { */ @Test public void testHandleRaftRPCWithNewerTerm() throws Exception { - new JavaTestKit(getSystem()) {{ + RaftActorContext actorContext = createActorContext(); - assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(getTestActor(), + assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(actorContext, behaviorActor, createAppendEntriesWithNewerTerm()); - assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(getTestActor(), + assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(actorContext, behaviorActor, createAppendEntriesReplyWithNewerTerm()); - assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(getTestActor(), + assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(actorContext, behaviorActor, createRequestVoteWithNewerTerm()); - assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(getTestActor(), + assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(actorContext, behaviorActor, createRequestVoteReplyWithNewerTerm()); - - - }}; } @@ -67,145 +80,95 @@ public abstract class AbstractRaftActorBehaviorTest extends AbstractActorTest { * @throws Exception */ @Test - public void testHandleAppendEntriesSenderTermLessThanReceiverTerm() - throws Exception { - new JavaTestKit(getSystem()) {{ - + public void testHandleAppendEntriesSenderTermLessThanReceiverTerm() throws Exception { MockRaftActorContext context = createActorContext(); // First set the receivers term to a high number (1000) context.getTermInformation().update(1000, "test"); - AppendEntries appendEntries = - new AppendEntries(100, "leader-1", 0, 0, null, 101, -1); + AppendEntries appendEntries = new AppendEntries(100, "leader-1", 0, 0, null, 101, -1); - RaftActorBehavior behavior = createBehavior(context); + behavior = createBehavior(context); // Send an unknown message so that the state of the RaftActor remains unchanged - RaftActorBehavior expected = behavior.handleMessage(getRef(), "unknown"); + RaftActorBehavior expected = behavior.handleMessage(behaviorActor, "unknown"); - RaftActorBehavior raftBehavior = - behavior.handleMessage(getRef(), appendEntries); + RaftActorBehavior raftBehavior = behavior.handleMessage(behaviorActor, appendEntries); - assertEquals(expected, raftBehavior); + assertEquals("Raft state", expected.state(), raftBehavior.state()); // Also expect an AppendEntriesReply to be sent where success is false - final Boolean out = new ExpectMsg(duration("1 seconds"), - "AppendEntriesReply") { - // do not put code outside this method, will run afterwards - @Override - protected Boolean match(Object in) { - if (in instanceof AppendEntriesReply) { - AppendEntriesReply reply = (AppendEntriesReply) in; - return reply.isSuccess(); - } else { - throw noMatch(); - } - } - }.get(); - - assertEquals(false, out); - - - }}; - } + AppendEntriesReply reply = MessageCollectorActor.expectFirstMatching( + behaviorActor, AppendEntriesReply.class); - @Test - public void testHandleAppendEntriesAddSameEntryToLog(){ - new JavaTestKit(getSystem()) { - { + assertEquals("isSuccess", false, reply.isSuccess()); + } - MockRaftActorContext context = createActorContext(); - // First set the receivers term to lower number - context.getTermInformation().update(2, "test"); + @Test + public void testHandleAppendEntriesAddSameEntryToLog() throws Exception { + MockRaftActorContext context = createActorContext(); - // Prepare the receivers log - MockRaftActorContext.SimpleReplicatedLog log = - new MockRaftActorContext.SimpleReplicatedLog(); - log.append( - new MockRaftActorContext.MockReplicatedLogEntry(1, 0, new MockRaftActorContext.MockPayload("zero"))); + context.getTermInformation().update(2, "test"); - context.setReplicatedLog(log); + // Prepare the receivers log + MockRaftActorContext.MockPayload payload = new MockRaftActorContext.MockPayload("zero"); + setLastLogEntry(context, 2, 0, payload); - List entries = new ArrayList<>(); - entries.add( - new MockRaftActorContext.MockReplicatedLogEntry(1, 0, new MockRaftActorContext.MockPayload("zero"))); + List entries = new ArrayList<>(); + entries.add(new MockRaftActorContext.MockReplicatedLogEntry(2, 0, payload)); - AppendEntries appendEntries = - new AppendEntries(2, "leader-1", -1, 1, entries, 0, -1); + AppendEntries appendEntries = new AppendEntries(2, "leader-1", -1, -1, entries, 2, -1); - RaftActorBehavior behavior = createBehavior(context); + behavior = createBehavior(context); - if (AbstractRaftActorBehaviorTest.this instanceof CandidateTest) { - // Resetting the Candidates term to make sure it will match - // the term sent by AppendEntries. If this was not done then - // the test will fail because the Candidate will assume that - // the message was sent to it from a lower term peer and will - // thus respond with a failure - context.getTermInformation().update(2, "test"); - } + if (behavior instanceof Candidate) { + // Resetting the Candidates term to make sure it will match + // the term sent by AppendEntries. If this was not done then + // the test will fail because the Candidate will assume that + // the message was sent to it from a lower term peer and will + // thus respond with a failure + context.getTermInformation().update(2, "test"); + } - // Send an unknown message so that the state of the RaftActor remains unchanged - RaftActorBehavior expected = behavior.handleMessage(getRef(), "unknown"); + // Send an unknown message so that the state of the RaftActor remains unchanged + RaftActorBehavior expected = behavior.handleMessage(behaviorActor, "unknown"); - RaftActorBehavior raftBehavior = - behavior.handleMessage(getRef(), appendEntries); + RaftActorBehavior raftBehavior = behavior.handleMessage(behaviorActor, appendEntries); - assertEquals(expected, raftBehavior); + assertEquals("Raft state", expected.state(), raftBehavior.state()); - assertEquals(1, log.size()); + assertEquals("ReplicatedLog size", 1, context.getReplicatedLog().size()); + handleAppendEntriesAddSameEntryToLogReply(behaviorActor); + } - }}; + protected void handleAppendEntriesAddSameEntryToLogReply(TestActorRef replyActor) + throws Exception { + AppendEntriesReply reply = MessageCollectorActor.getFirstMatching(replyActor, AppendEntriesReply.class); + Assert.assertNull("Expected no AppendEntriesReply", reply); } /** * This test verifies that when a RequestVote is received by the RaftActor - * with a term which is greater than the RaftActors' currentTerm and the - * senders' log is more upto date than the receiver that the receiver grants - * the vote to the sender + * with the senders' log is more up to date than the receiver that the receiver grants + * the vote to the sender. */ @Test - public void testHandleRequestVoteWhenSenderTermGreaterThanCurrentTermAndSenderLogMoreUpToDate() { - new JavaTestKit(getSystem()) {{ - - new Within(duration("1 seconds")) { - @Override - protected void run() { - - RaftActorBehavior behavior = createBehavior( - createActorContext(behaviorActor)); - - RaftActorBehavior raftBehavior = behavior.handleMessage(getTestActor(), - new RequestVote(1000, "test", 10000, 999)); - - if(!(behavior instanceof Follower)){ - assertTrue(raftBehavior instanceof Follower); - } else { - - final Boolean out = - new ExpectMsg(duration("1 seconds"), - "RequestVoteReply") { - // do not put code outside this method, will run afterwards - @Override - protected Boolean match(Object in) { - if (in instanceof RequestVoteReply) { - RequestVoteReply reply = - (RequestVoteReply) in; - return reply.isVoteGranted(); - } else { - throw noMatch(); - } - } - }.get(); - - assertEquals(true, out); - } - } - }; - }}; + public void testHandleRequestVoteWhenSenderLogMoreUpToDate() { + MockRaftActorContext context = createActorContext(); + + behavior = createBehavior(context); + + context.getTermInformation().update(1, "test"); + + behavior.handleMessage(behaviorActor, new RequestVote(context.getTermInformation().getCurrentTerm(), + "test", 10000, 999)); + + RequestVoteReply reply = MessageCollectorActor.expectFirstMatching(behaviorActor, + RequestVoteReply.class); + assertEquals("isVoteGranted", true, reply.isVoteGranted()); } /** @@ -214,53 +177,24 @@ public abstract class AbstractRaftActorBehaviorTest extends AbstractActorTest { * log then the receiving RaftActor will not grant the vote to the sender */ @Test - public void testHandleRequestVoteWhenSenderTermGreaterThanCurrentTermButSenderLogLessUptoDate() { - new JavaTestKit(getSystem()) {{ - - new Within(duration("1 seconds")) { - @Override - protected void run() { - - RaftActorContext actorContext = - createActorContext(behaviorActor); - - MockRaftActorContext.SimpleReplicatedLog - log = new MockRaftActorContext.SimpleReplicatedLog(); - log.append( - new MockRaftActorContext.MockReplicatedLogEntry(20000, - 1000000, new MockRaftActorContext.MockPayload(""))); - - ((MockRaftActorContext) actorContext).setReplicatedLog(log); - - RaftActorBehavior behavior = createBehavior(actorContext); - - RaftActorBehavior raftBehavior = behavior.handleMessage(getTestActor(), - new RequestVote(1000, "test", 10000, 999)); - - if(!(behavior instanceof Follower)){ - assertTrue(raftBehavior instanceof Follower); - } else { - final Boolean out = - new ExpectMsg(duration("1 seconds"), - "RequestVoteReply") { - // do not put code outside this method, will run afterwards - @Override - protected Boolean match(Object in) { - if (in instanceof RequestVoteReply) { - RequestVoteReply reply = - (RequestVoteReply) in; - return reply.isVoteGranted(); - } else { - throw noMatch(); - } - } - }.get(); - - assertEquals(false, out); - } - } - }; - }}; + public void testHandleRequestVoteWhenSenderLogLessUptoDate() { + MockRaftActorContext context = createActorContext(); + + behavior = createBehavior(context); + + context.getTermInformation().update(1, "test"); + + int index = 2000; + setLastLogEntry(context, context.getTermInformation().getCurrentTerm(), index, + new MockRaftActorContext.MockPayload("")); + + behavior.handleMessage(behaviorActor, new RequestVote( + context.getTermInformation().getCurrentTerm(), "test", + index - 1, context.getTermInformation().getCurrentTerm())); + + RequestVoteReply reply = MessageCollectorActor.expectFirstMatching(behaviorActor, + RequestVoteReply.class); + assertEquals("isVoteGranted", false, reply.isVoteGranted()); } @@ -272,42 +206,17 @@ public abstract class AbstractRaftActorBehaviorTest extends AbstractActorTest { */ @Test public void testHandleRequestVoteWhenSenderTermLessThanCurrentTerm() { - new JavaTestKit(getSystem()) {{ - - new Within(duration("1 seconds")) { - @Override - protected void run() { - - RaftActorContext context = - createActorContext(behaviorActor); - - context.getTermInformation().update(1000, null); - - RaftActorBehavior follower = createBehavior(context); - - follower.handleMessage(getTestActor(), - new RequestVote(999, "test", 10000, 999)); - - final Boolean out = - new ExpectMsg(duration("1 seconds"), - "RequestVoteReply") { - // do not put code outside this method, will run afterwards - @Override - protected Boolean match(Object in) { - if (in instanceof RequestVoteReply) { - RequestVoteReply reply = - (RequestVoteReply) in; - return reply.isVoteGranted(); - } else { - throw noMatch(); - } - } - }.get(); - - assertEquals(false, out); - } - }; - }}; + RaftActorContext context = createActorContext(); + + context.getTermInformation().update(1000, null); + + behavior = createBehavior(context); + + behavior.handleMessage(behaviorActor, new RequestVote(999, "test", 10000, 999)); + + RequestVoteReply reply = MessageCollectorActor.expectFirstMatching(behaviorActor, + RequestVoteReply.class); + assertEquals("isVoteGranted", false, reply.isVoteGranted()); } @Test @@ -357,18 +266,21 @@ public abstract class AbstractRaftActorBehaviorTest extends AbstractActorTest { } - protected void assertStateChangesToFollowerWhenRaftRPCHasNewerTerm( - ActorRef actorRef, RaftRPC rpc) { + protected void assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(RaftActorContext actorContext, + ActorRef actorRef, RaftRPC rpc) throws Exception { - RaftActorContext actorContext = createActorContext(); Payload p = new MockRaftActorContext.MockPayload(""); - setLastLogEntry( - (MockRaftActorContext) actorContext, 0, 0, p); + setLastLogEntry((MockRaftActorContext) actorContext, 1, 0, p); + actorContext.getTermInformation().update(1, "test"); - RaftActorBehavior raftBehavior = createBehavior(actorContext) - .handleMessage(actorRef, rpc); + RaftActorBehavior origBehavior = createBehavior(actorContext); + RaftActorBehavior raftBehavior = origBehavior.handleMessage(actorRef, rpc); - assertTrue(raftBehavior instanceof Follower); + assertEquals("New raft state", RaftState.Follower, raftBehavior.state()); + assertEquals("New election term", rpc.getTerm(), actorContext.getTermInformation().getCurrentTerm()); + + origBehavior.close(); + raftBehavior.close(); } protected MockRaftActorContext.SimpleReplicatedLog setLastLogEntry( @@ -377,10 +289,9 @@ public abstract class AbstractRaftActorBehaviorTest extends AbstractActorTest { new MockRaftActorContext.MockReplicatedLogEntry(term, index, data)); } - protected MockRaftActorContext.SimpleReplicatedLog setLastLogEntry( - MockRaftActorContext actorContext, ReplicatedLogEntry logEntry) { - MockRaftActorContext.SimpleReplicatedLog - log = new MockRaftActorContext.SimpleReplicatedLog(); + protected MockRaftActorContext.SimpleReplicatedLog setLastLogEntry(MockRaftActorContext actorContext, + ReplicatedLogEntry logEntry) { + MockRaftActorContext.SimpleReplicatedLog log = new MockRaftActorContext.SimpleReplicatedLog(); log.append(logEntry); actorContext.setReplicatedLog(log); 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 9d323b6ab1..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,6 +1,7 @@ package org.opendaylight.controller.cluster.raft.behaviors; import static org.junit.Assert.assertEquals; +import akka.actor.ActorRef; import akka.actor.Props; import akka.testkit.TestActorRef; import java.util.Collections; @@ -13,18 +14,16 @@ 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.ReplicatedLogEntry; -import org.opendaylight.controller.cluster.raft.TestActorFactory; 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.MessageCollectorActor; public class CandidateTest extends AbstractRaftActorBehaviorTest { - private final TestActorFactory actorFactory = new TestActorFactory(getSystem()); - private final TestActorRef candidateActor = actorFactory.createTestActor( Props.create(MessageCollectorActor.class), actorFactory.generateActorId("candidate")); @@ -36,13 +35,14 @@ public class CandidateTest extends AbstractRaftActorBehaviorTest { public void setUp(){ } + @Override @After public void tearDown() throws Exception { if(candidate != null) { candidate.close(); } - actorFactory.close(); + super.tearDown(); } @Test @@ -206,4 +206,11 @@ public class CandidateTest extends AbstractRaftActorBehaviorTest { 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()); + } } diff --git a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/FollowerTest.java b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/FollowerTest.java index 34cdd5b67e..4e8e7fe11b 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/FollowerTest.java +++ b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/FollowerTest.java @@ -17,21 +17,19 @@ import org.opendaylight.controller.cluster.raft.MockRaftActorContext; import org.opendaylight.controller.cluster.raft.RaftActorContext; import org.opendaylight.controller.cluster.raft.ReplicatedLogEntry; import org.opendaylight.controller.cluster.raft.Snapshot; -import org.opendaylight.controller.cluster.raft.TestActorFactory; import org.opendaylight.controller.cluster.raft.base.messages.ApplySnapshot; 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.InstallSnapshot; import org.opendaylight.controller.cluster.raft.messages.InstallSnapshotReply; +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.MessageCollectorActor; public class FollowerTest extends AbstractRaftActorBehaviorTest { - private final TestActorFactory actorFactory = new TestActorFactory(getSystem()); - private final TestActorRef followerActor = actorFactory.createTestActor( Props.create(MessageCollectorActor.class), actorFactory.generateActorId("follower")); @@ -40,13 +38,14 @@ public class FollowerTest extends AbstractRaftActorBehaviorTest { private RaftActorBehavior follower; + @Override @After public void tearDown() throws Exception { if(follower != null) { follower.close(); } - actorFactory.close(); + super.tearDown(); } @Override @@ -514,4 +513,20 @@ public class FollowerTest extends AbstractRaftActorBehaviorTest { return new MockRaftActorContext.MockReplicatedLogEntry(term, index, new MockRaftActorContext.MockPayload(data)); } + + @Override + protected void assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(RaftActorContext actorContext, + ActorRef actorRef, RaftRPC rpc) throws Exception { + super.assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(actorContext, actorRef, rpc); + + String expVotedFor = RequestVote.class.isInstance(rpc) ? ((RequestVote)rpc).getCandidateId() : null; + assertEquals("New votedFor", expVotedFor, actorContext.getTermInformation().getVotedFor()); + } + + @Override + protected void handleAppendEntriesAddSameEntryToLogReply(TestActorRef replyActor) + throws Exception { + AppendEntriesReply reply = MessageCollectorActor.expectFirstMatching(replyActor, AppendEntriesReply.class); + assertEquals("isSuccess", true, reply.isSuccess()); + } } diff --git a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/IsolatedLeaderTest.java b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/IsolatedLeaderTest.java index 6197f980ea..b37ace9560 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/IsolatedLeaderTest.java +++ b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/IsolatedLeaderTest.java @@ -8,30 +8,42 @@ package org.opendaylight.controller.cluster.raft.behaviors; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; import akka.actor.ActorRef; import akka.actor.Props; -import akka.testkit.JavaTestKit; +import akka.testkit.TestActorRef; import java.util.HashMap; import java.util.Map; +import org.junit.After; import org.junit.Test; +import org.opendaylight.controller.cluster.raft.DefaultConfigParamsImpl; 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.messages.AppendEntriesReply; -import org.opendaylight.controller.cluster.raft.utils.DoNothingActor; +import org.opendaylight.controller.cluster.raft.utils.MessageCollectorActor; public class IsolatedLeaderTest extends AbstractRaftActorBehaviorTest { - private final ActorRef leaderActor = - getSystem().actorOf(Props.create(DoNothingActor.class)); + private final TestActorRef leaderActor = actorFactory.createTestActor( + Props.create(MessageCollectorActor.class), actorFactory.generateActorId("leader")); - private final ActorRef senderActor = - getSystem().actorOf(Props.create(DoNothingActor.class)); + private final TestActorRef senderActor = actorFactory.createTestActor( + Props.create(MessageCollectorActor.class), actorFactory.generateActorId("sender")); + + private AbstractLeader isolatedLeader; + + @Override + @After + public void tearDown() throws Exception { + if(isolatedLeader != null) { + isolatedLeader.close(); + } + + super.tearDown(); + } @Override - protected RaftActorBehavior createBehavior( - RaftActorContext actorContext) { + protected RaftActorBehavior createBehavior(RaftActorContext actorContext) { return new Leader(actorContext); } @@ -40,102 +52,110 @@ public class IsolatedLeaderTest extends AbstractRaftActorBehaviorTest { return createActorContext(leaderActor); } + @Override + protected MockRaftActorContext createActorContext(ActorRef actor) { + DefaultConfigParamsImpl configParams = new DefaultConfigParamsImpl(); + configParams.setElectionTimeoutFactor(100000); + MockRaftActorContext context = new MockRaftActorContext("isolated-leader", getSystem(), actor); + context.setConfigParams(configParams); + return context; + } @Test - public void testHandleMessageWithThreeMembers() { - new JavaTestKit(getSystem()) {{ - String followerAddress1 = "akka://test/user/$a"; - String followerAddress2 = "akka://test/user/$b"; - - MockRaftActorContext leaderActorContext = createActorContext(); - Map peerAddresses = new HashMap<>(); - peerAddresses.put("follower-1", followerAddress1); - peerAddresses.put("follower-2", followerAddress2); - leaderActorContext.setPeerAddresses(peerAddresses); - - IsolatedLeader isolatedLeader = new IsolatedLeader(leaderActorContext); - assertTrue(isolatedLeader.state() == RaftState.IsolatedLeader); - - // in a 3 node cluster, even if 1 follower is returns a reply, the isolatedLeader is not isolated - RaftActorBehavior behavior = isolatedLeader.handleMessage(senderActor, + public void testHandleMessageWithThreeMembers() throws Exception { + String followerAddress1 = "akka://test/user/$a"; + String followerAddress2 = "akka://test/user/$b"; + + MockRaftActorContext leaderActorContext = createActorContext(); + Map peerAddresses = new HashMap<>(); + peerAddresses.put("follower-1", followerAddress1); + peerAddresses.put("follower-2", followerAddress2); + leaderActorContext.setPeerAddresses(peerAddresses); + + isolatedLeader = new IsolatedLeader(leaderActorContext); + assertEquals("Raft state", RaftState.IsolatedLeader, isolatedLeader.state()); + + // in a 3 node cluster, even if 1 follower is returns a reply, the isolatedLeader is not isolated + RaftActorBehavior behavior = isolatedLeader.handleMessage(senderActor, new AppendEntriesReply("follower-1", isolatedLeader.lastTerm() - 1, true, - isolatedLeader.lastIndex() - 1, isolatedLeader.lastTerm() - 1)); + isolatedLeader.lastIndex() - 1, isolatedLeader.lastTerm() - 1)); + + assertEquals("Raft state", RaftState.Leader, behavior.state()); - assertEquals(RaftState.Leader, behavior.state()); + isolatedLeader.close(); + isolatedLeader = (AbstractLeader) behavior; - behavior = isolatedLeader.handleMessage(senderActor, + behavior = isolatedLeader.handleMessage(senderActor, new AppendEntriesReply("follower-2", isolatedLeader.lastTerm() - 1, true, - isolatedLeader.lastIndex() -1, isolatedLeader.lastTerm() -1 )); + isolatedLeader.lastIndex() -1, isolatedLeader.lastTerm() -1 )); - assertEquals(RaftState.Leader, behavior.state()); - }}; + assertEquals("Raft state", RaftState.Leader, behavior.state()); } @Test - public void testHandleMessageWithFiveMembers() { - new JavaTestKit(getSystem()) {{ - - String followerAddress1 = "akka://test/user/$a"; - String followerAddress2 = "akka://test/user/$b"; - String followerAddress3 = "akka://test/user/$c"; - String followerAddress4 = "akka://test/user/$d"; - - MockRaftActorContext leaderActorContext = createActorContext(); - Map peerAddresses = new HashMap<>(); - peerAddresses.put("follower-1", followerAddress1); - peerAddresses.put("follower-2", followerAddress2); - peerAddresses.put("follower-3", followerAddress3); - peerAddresses.put("follower-4", followerAddress4); - leaderActorContext.setPeerAddresses(peerAddresses); - - IsolatedLeader isolatedLeader = new IsolatedLeader(leaderActorContext); - assertEquals(RaftState.IsolatedLeader, isolatedLeader.state()); - - // in a 5 member cluster, atleast 2 followers need to be active and return a reply - RaftActorBehavior behavior = isolatedLeader.handleMessage(senderActor, + public void testHandleMessageWithFiveMembers() throws Exception { + String followerAddress1 = "akka://test/user/$a"; + String followerAddress2 = "akka://test/user/$b"; + String followerAddress3 = "akka://test/user/$c"; + String followerAddress4 = "akka://test/user/$d"; + + MockRaftActorContext leaderActorContext = createActorContext(); + Map peerAddresses = new HashMap<>(); + peerAddresses.put("follower-1", followerAddress1); + peerAddresses.put("follower-2", followerAddress2); + peerAddresses.put("follower-3", followerAddress3); + peerAddresses.put("follower-4", followerAddress4); + leaderActorContext.setPeerAddresses(peerAddresses); + + isolatedLeader = new IsolatedLeader(leaderActorContext); + assertEquals("Raft state", RaftState.IsolatedLeader, isolatedLeader.state()); + + // in a 5 member cluster, atleast 2 followers need to be active and return a reply + RaftActorBehavior behavior = isolatedLeader.handleMessage(senderActor, new AppendEntriesReply("follower-1", isolatedLeader.lastTerm() - 1, true, - isolatedLeader.lastIndex() -1, isolatedLeader.lastTerm() -1 )); + isolatedLeader.lastIndex() -1, isolatedLeader.lastTerm() -1 )); - assertEquals(RaftState.IsolatedLeader, behavior.state()); + assertEquals("Raft state", RaftState.IsolatedLeader, behavior.state()); - behavior = isolatedLeader.handleMessage(senderActor, + behavior = isolatedLeader.handleMessage(senderActor, new AppendEntriesReply("follower-2", isolatedLeader.lastTerm() - 1, true, - isolatedLeader.lastIndex() -1, isolatedLeader.lastTerm() -1 )); + isolatedLeader.lastIndex() -1, isolatedLeader.lastTerm() -1 )); + + assertEquals("Raft state", RaftState.Leader, behavior.state()); - assertEquals(RaftState.Leader, behavior.state()); + isolatedLeader.close(); + isolatedLeader = (AbstractLeader) behavior; - behavior = isolatedLeader.handleMessage(senderActor, + behavior = isolatedLeader.handleMessage(senderActor, new AppendEntriesReply("follower-3", isolatedLeader.lastTerm() - 1, true, - isolatedLeader.lastIndex() -1, isolatedLeader.lastTerm() -1 )); + isolatedLeader.lastIndex() -1, isolatedLeader.lastTerm() -1 )); - assertEquals(RaftState.Leader, behavior.state()); - }}; + assertEquals("Raft state", RaftState.Leader, behavior.state()); } @Test - public void testHandleMessageFromAnotherLeader() { - new JavaTestKit(getSystem()) {{ - String followerAddress1 = "akka://test/user/$a"; - String followerAddress2 = "akka://test/user/$b"; - - MockRaftActorContext leaderActorContext = createActorContext(); - Map peerAddresses = new HashMap<>(); - peerAddresses.put("follower-1", followerAddress1); - peerAddresses.put("follower-2", followerAddress2); - leaderActorContext.setPeerAddresses(peerAddresses); - - IsolatedLeader isolatedLeader = new IsolatedLeader(leaderActorContext); - assertTrue(isolatedLeader.state() == RaftState.IsolatedLeader); - - // if an append-entries reply is received by the isolated-leader, and that reply - // has a term > than its own term, then IsolatedLeader switches to Follower - // bowing itself to another leader in the cluster - RaftActorBehavior behavior = isolatedLeader.handleMessage(senderActor, + public void testHandleMessageFromAnotherLeader() throws Exception { + String followerAddress1 = "akka://test/user/$a"; + String followerAddress2 = "akka://test/user/$b"; + + MockRaftActorContext leaderActorContext = createActorContext(); + Map peerAddresses = new HashMap<>(); + peerAddresses.put("follower-1", followerAddress1); + peerAddresses.put("follower-2", followerAddress2); + leaderActorContext.setPeerAddresses(peerAddresses); + + isolatedLeader = new IsolatedLeader(leaderActorContext); + assertEquals("Raft state", RaftState.IsolatedLeader, isolatedLeader.state()); + + // if an append-entries reply is received by the isolated-leader, and that reply + // has a term > than its own term, then IsolatedLeader switches to Follower + // bowing itself to another leader in the cluster + RaftActorBehavior behavior = isolatedLeader.handleMessage(senderActor, new AppendEntriesReply("follower-1", isolatedLeader.lastTerm() + 1, true, - isolatedLeader.lastIndex() + 1, isolatedLeader.lastTerm() + 1)); + isolatedLeader.lastIndex() + 1, isolatedLeader.lastTerm() + 1)); - assertEquals(RaftState.Follower, behavior.state()); - }}; + assertEquals("Raft state", RaftState.Follower, behavior.state()); + behavior.close(); } } diff --git a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/LeaderTest.java b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/LeaderTest.java index 02c391f146..853ed5867d 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/LeaderTest.java +++ b/opendaylight/md-sal/sal-akka-raft/src/test/java/org/opendaylight/controller/cluster/raft/behaviors/LeaderTest.java @@ -27,7 +27,6 @@ import org.opendaylight.controller.cluster.raft.RaftActorContext; import org.opendaylight.controller.cluster.raft.RaftState; import org.opendaylight.controller.cluster.raft.ReplicatedLogImplEntry; import org.opendaylight.controller.cluster.raft.SerializationUtils; -import org.opendaylight.controller.cluster.raft.TestActorFactory; import org.opendaylight.controller.cluster.raft.base.messages.ApplyLogEntries; import org.opendaylight.controller.cluster.raft.base.messages.ApplyState; import org.opendaylight.controller.cluster.raft.base.messages.CaptureSnapshot; @@ -40,6 +39,7 @@ import org.opendaylight.controller.cluster.raft.messages.AppendEntries; import org.opendaylight.controller.cluster.raft.messages.AppendEntriesReply; import org.opendaylight.controller.cluster.raft.messages.InstallSnapshot; import org.opendaylight.controller.cluster.raft.messages.InstallSnapshotReply; +import org.opendaylight.controller.cluster.raft.messages.RaftRPC; import org.opendaylight.controller.cluster.raft.messages.RequestVoteReply; import org.opendaylight.controller.cluster.raft.utils.MessageCollectorActor; import org.opendaylight.controller.protobuff.messages.cluster.raft.InstallSnapshotMessages; @@ -49,8 +49,6 @@ public class LeaderTest extends AbstractRaftActorBehaviorTest { static final String FOLLOWER_ID = "follower"; - private final TestActorFactory actorFactory = new TestActorFactory(getSystem()); - private final TestActorRef leaderActor = actorFactory.createTestActor( Props.create(ForwardMessageToBehaviorActor.class), actorFactory.generateActorId("leader")); @@ -59,13 +57,14 @@ public class LeaderTest extends AbstractRaftActorBehaviorTest { private Leader leader; + @Override @After public void tearDown() throws Exception { if(leader != null) { leader.close(); } - actorFactory.close(); + super.tearDown(); } @Test @@ -1136,6 +1135,13 @@ public class LeaderTest extends AbstractRaftActorBehaviorTest { follower.close(); } + @Override + protected void assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(RaftActorContext actorContext, + ActorRef actorRef, RaftRPC rpc) throws Exception { + super.assertStateChangesToFollowerWhenRaftRPCHasNewerTerm(actorContext, actorRef, rpc); + assertEquals("New votedFor", null, actorContext.getTermInformation().getVotedFor()); + } + private class MockConfigParamsImpl extends DefaultConfigParamsImpl { private final long electionTimeOutIntervalMillis;