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%2FAbstractRaftActorBehaviorTest.java;h=d3f5a0eead1ff3ccdd2e69f215eccfb6886143b4;hp=c133c0615f0c770feea88daa1b8b82fa5cf8f661;hb=f12d62d2dc28a883c1f1b38df7d72a9142c2abfb;hpb=4f295e3104c4b0c3fccc4f7ac8010dbddf613ef7 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 c133c0615f..d3f5a0eead 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,30 +1,52 @@ 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; +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 @@ -34,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()); - - - }}; } @@ -61,144 +80,98 @@ public abstract class AbstractRaftActorBehaviorTest extends AbstractActorTest { * @throws Exception */ @Test - public void testHandleAppendEntriesSenderTermLessThanReceiverTerm() - throws Exception { - new JavaTestKit(getSystem()) {{ - - MockRaftActorContext context = (MockRaftActorContext) - createActorContext(); + public void testHandleAppendEntriesSenderTermLessThanReceiverTerm() throws Exception { + MockRaftActorContext context = createActorContext(); + short payloadVersion = 5; + context.setPayloadVersion(payloadVersion); - // First set the receivers term to a high number (1000) - context.getTermInformation().update(1000, "test"); + // 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, (short)4); - 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"); + // 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()); - // 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 - protected Boolean match(Object in) { - if (in instanceof AppendEntriesReply) { - AppendEntriesReply reply = (AppendEntriesReply) in; - return reply.isSuccess(); - } else { - throw noMatch(); - } - } - }.get(); + // Also expect an AppendEntriesReply to be sent where success is false - assertEquals(false, out); + AppendEntriesReply reply = MessageCollectorActor.expectFirstMatching( + behaviorActor, AppendEntriesReply.class); - - }}; + assertEquals("isSuccess", false, reply.isSuccess()); + assertEquals("getPayloadVersion", payloadVersion, reply.getPayloadVersion()); } @Test - public void testHandleAppendEntriesAddSameEntryToLog(){ - new JavaTestKit(getSystem()) { - { - - MockRaftActorContext context = (MockRaftActorContext) - createActorContext(); - - // First set the receivers term to lower number - context.getTermInformation().update(2, "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, (short)0); - 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")) { - 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 - 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()); } /** @@ -207,51 +180,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")) { - 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 - 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()); } @@ -263,40 +209,17 @@ public abstract class AbstractRaftActorBehaviorTest extends AbstractActorTest { */ @Test public void testHandleRequestVoteWhenSenderTermLessThanCurrentTerm() { - new JavaTestKit(getSystem()) {{ - - new Within(duration("1 seconds")) { - 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 - 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 @@ -346,18 +269,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 origBehavior = createBehavior(actorContext); + RaftActorBehavior raftBehavior = origBehavior.handleMessage(actorRef, rpc); - RaftActorBehavior raftBehavior = createBehavior(actorContext) - .handleMessage(actorRef, rpc); + assertEquals("New raft state", RaftState.Follower, raftBehavior.state()); + assertEquals("New election term", rpc.getTerm(), actorContext.getTermInformation().getCurrentTerm()); - assertTrue(raftBehavior instanceof Follower); + origBehavior.close(); + raftBehavior.close(); } protected MockRaftActorContext.SimpleReplicatedLog setLastLogEntry( @@ -366,10 +292,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); @@ -383,20 +308,20 @@ public abstract class AbstractRaftActorBehaviorTest extends AbstractActorTest { return createBehavior(createActorContext()); } - protected RaftActorContext createActorContext() { + protected MockRaftActorContext createActorContext() { return new MockRaftActorContext(); } - protected RaftActorContext createActorContext(ActorRef actor) { + protected MockRaftActorContext createActorContext(ActorRef actor) { return new MockRaftActorContext("test", getSystem(), actor); } protected AppendEntries createAppendEntriesWithNewerTerm() { - return new AppendEntries(100, "leader-1", 0, 0, null, 1, -1); + return new AppendEntries(100, "leader-1", 0, 0, null, 1, -1, (short)0); } protected AppendEntriesReply createAppendEntriesReplyWithNewerTerm() { - return new AppendEntriesReply("follower-1", 100, false, 100, 100); + return new AppendEntriesReply("follower-1", 100, false, 100, 100, (short)0); } protected RequestVote createRequestVoteWithNewerTerm() { @@ -410,4 +335,18 @@ public abstract class AbstractRaftActorBehaviorTest extends AbstractActorTest { protected Object fromSerializableMessage(Object serializable){ return SerializationUtils.fromSerializable(serializable); } + + protected ByteString toByteString(Map state) { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + try(ObjectOutputStream oos = new ObjectOutputStream(bos)) { + oos.writeObject(state); + return ByteString.copyFrom(bos.toByteArray()); + } catch (IOException e) { + throw new AssertionError("IOException occurred converting Map to Bytestring", e); + } + } + + protected void logStart(String name) { + LoggerFactory.getLogger(LeaderTest.class).info("Starting " + name); + } }