pekko/akka-actor-tests/src/test/scala/akka/pattern/BackoffSupervisorSpec.scala

223 lines
7 KiB
Scala
Raw Normal View History

/**
2018-01-04 17:26:29 +00:00
* Copyright (C) 2015-2018 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.pattern
import scala.concurrent.duration._
import akka.actor._
import akka.testkit._
import scala.util.control.NoStackTrace
import akka.actor.SupervisorStrategy
object BackoffSupervisorSpec {
class TestException extends RuntimeException with NoStackTrace
object Child {
def props(probe: ActorRef): Props =
Props(new Child(probe))
}
class Child(probe: ActorRef) extends Actor {
def receive = {
case "boom" throw new TestException
case msg probe ! msg
}
}
object ManualChild {
def props(probe: ActorRef): Props =
Props(new ManualChild(probe))
}
class ManualChild(probe: ActorRef) extends Actor {
def receive = {
case "boom" throw new TestException
case msg
probe ! msg
context.parent ! BackoffSupervisor.Reset
}
}
}
class BackoffSupervisorSpec extends AkkaSpec with ImplicitSender {
import BackoffSupervisorSpec._
def onStopOptions(props: Props = Child.props(testActor)) = Backoff.onStop(props, "c1", 100.millis, 3.seconds, 0.2)
def onFailureOptions(props: Props = Child.props(testActor)) = Backoff.onFailure(props, "c1", 100.millis, 3.seconds, 0.2)
def create(options: BackoffOptions) = system.actorOf(BackoffSupervisor.props(options))
"BackoffSupervisor" must {
"start child again when it stops when using `Backoff.onStop`" in {
val supervisor = create(onStopOptions())
supervisor ! BackoffSupervisor.GetCurrentChild
val c1 = expectMsgType[BackoffSupervisor.CurrentChild].ref.get
watch(c1)
c1 ! PoisonPill
expectTerminated(c1)
awaitAssert {
supervisor ! BackoffSupervisor.GetCurrentChild
// new instance
expectMsgType[BackoffSupervisor.CurrentChild].ref.get should !==(c1)
}
}
"forward messages to the child" in {
def assertForward(supervisor: ActorRef) = {
supervisor ! "hello"
expectMsg("hello")
}
assertForward(create(onStopOptions()))
assertForward(create(onFailureOptions()))
}
"support custom supervision strategy" in {
def assertCustomStrategy(supervisor: ActorRef) = {
supervisor ! BackoffSupervisor.GetCurrentChild
val c1 = expectMsgType[BackoffSupervisor.CurrentChild].ref.get
watch(c1)
c1 ! "boom"
expectTerminated(c1)
awaitAssert {
supervisor ! BackoffSupervisor.GetCurrentChild
// new instance
expectMsgType[BackoffSupervisor.CurrentChild].ref.get should !==(c1)
}
}
filterException[TestException] {
val stoppingStrategy = OneForOneStrategy() {
case _: TestException SupervisorStrategy.Stop
}
val restartingStrategy = OneForOneStrategy() {
case _: TestException SupervisorStrategy.Restart
}
assertCustomStrategy(
create(onStopOptions()
.withSupervisorStrategy(stoppingStrategy)))
assertCustomStrategy(
create(onFailureOptions()
.withSupervisorStrategy(restartingStrategy)))
}
}
"support default stopping strategy when using `Backoff.onStop`" in {
filterException[TestException] {
val supervisor = create(onStopOptions().withDefaultStoppingStrategy)
supervisor ! BackoffSupervisor.GetCurrentChild
val c1 = expectMsgType[BackoffSupervisor.CurrentChild].ref.get
watch(c1)
supervisor ! BackoffSupervisor.GetRestartCount
expectMsg(BackoffSupervisor.RestartCount(0))
c1 ! "boom"
expectTerminated(c1)
awaitAssert {
supervisor ! BackoffSupervisor.GetCurrentChild
// new instance
expectMsgType[BackoffSupervisor.CurrentChild].ref.get should !==(c1)
}
supervisor ! BackoffSupervisor.GetRestartCount
expectMsg(BackoffSupervisor.RestartCount(1))
}
}
"support manual reset" in {
filterException[TestException] {
def assertManualReset(supervisor: ActorRef) = {
supervisor ! BackoffSupervisor.GetCurrentChild
val c1 = expectMsgType[BackoffSupervisor.CurrentChild].ref.get
watch(c1)
c1 ! "boom"
expectTerminated(c1)
awaitAssert {
supervisor ! BackoffSupervisor.GetRestartCount
expectMsg(BackoffSupervisor.RestartCount(1))
}
awaitAssert {
supervisor ! BackoffSupervisor.GetCurrentChild
// new instance
expectMsgType[BackoffSupervisor.CurrentChild].ref.get should !==(c1)
}
supervisor ! "hello"
expectMsg("hello")
// making sure the Reset is handled by supervisor
supervisor ! "hello"
expectMsg("hello")
supervisor ! BackoffSupervisor.GetRestartCount
expectMsg(BackoffSupervisor.RestartCount(0))
}
val stoppingStrategy = OneForOneStrategy() {
case _: TestException SupervisorStrategy.Stop
}
val restartingStrategy = OneForOneStrategy() {
case _: TestException SupervisorStrategy.Restart
}
assertManualReset(
create(onStopOptions(ManualChild.props(testActor))
.withManualReset
.withSupervisorStrategy(stoppingStrategy)))
assertManualReset(
create(onFailureOptions(ManualChild.props(testActor))
.withManualReset
.withSupervisorStrategy(restartingStrategy)))
}
}
"reply to sender if replyWhileStopped is specified" in {
filterException[TestException] {
val supervisor = create(Backoff.onFailure(Child.props(testActor), "c1", 100.seconds, 300.seconds, 0.2).withReplyWhileStopped("child was stopped"))
supervisor ! BackoffSupervisor.GetCurrentChild
val c1 = expectMsgType[BackoffSupervisor.CurrentChild].ref.get
watch(c1)
supervisor ! BackoffSupervisor.GetRestartCount
expectMsg(BackoffSupervisor.RestartCount(0))
c1 ! "boom"
expectTerminated(c1)
awaitAssert {
supervisor ! BackoffSupervisor.GetRestartCount
expectMsg(BackoffSupervisor.RestartCount(1))
}
supervisor ! "boom"
expectMsg("child was stopped")
}
}
"not reply to sender if replyWhileStopped is NOT specified" in {
filterException[TestException] {
val supervisor = create(Backoff.onFailure(Child.props(testActor), "c1", 100.seconds, 300.seconds, 0.2))
supervisor ! BackoffSupervisor.GetCurrentChild
val c1 = expectMsgType[BackoffSupervisor.CurrentChild].ref.get
watch(c1)
supervisor ! BackoffSupervisor.GetRestartCount
expectMsg(BackoffSupervisor.RestartCount(0))
c1 ! "boom"
expectTerminated(c1)
awaitAssert {
supervisor ! BackoffSupervisor.GetRestartCount
expectMsg(BackoffSupervisor.RestartCount(1))
}
supervisor ! "boom" //this will be sent to deadLetters
expectNoMsg(500.milliseconds)
}
}
}
}