From 3b01e089047daad75e96e4670b50db04e5a057a9 Mon Sep 17 00:00:00 2001 From: Josep Prat Date: Fri, 28 Jul 2017 09:53:37 +0200 Subject: [PATCH] Typed test kit improvements #23360 * Call right overloaded super method in spawn and spawnAnonymous (avoiding infinite recursion) * Add Effects for each type of Spawning --- .../scala/akka/typed/testkit/Effects.scala | 26 +++- .../testkit/EffectfulActorContextSpec.scala | 138 ++++++++++++++++++ 2 files changed, 156 insertions(+), 8 deletions(-) create mode 100644 akka-typed-testkit/src/test/scala/akka/typed/testkit/EffectfulActorContextSpec.scala diff --git a/akka-typed-testkit/src/main/scala/akka/typed/testkit/Effects.scala b/akka-typed-testkit/src/main/scala/akka/typed/testkit/Effects.scala index 2a0be055f2..c551dc42f3 100644 --- a/akka-typed-testkit/src/main/scala/akka/typed/testkit/Effects.scala +++ b/akka-typed-testkit/src/main/scala/akka/typed/testkit/Effects.scala @@ -5,7 +5,7 @@ package akka.typed.testkit import java.util.concurrent.ConcurrentLinkedQueue -import akka.typed.{ ActorContext, ActorRef, ActorSystem, Behavior, EmptyProps, PostStop, Props, Signal } +import akka.typed.{ ActorContext, ActorRef, ActorSystem, Behavior, PostStop, Props, Signal } import scala.annotation.tailrec import scala.collection.immutable @@ -20,7 +20,12 @@ import scala.concurrent.duration.{ Duration, FiniteDuration } abstract class Effect object Effect { - @SerialVersionUID(1L) final case class Spawned(childName: String) extends Effect + + abstract class SpawnedEffect extends Effect + + @SerialVersionUID(1L) final case class Spawned(childName: String, props: Props) extends SpawnedEffect + @SerialVersionUID(1L) final case class SpawnedAnonymous(props: Props) extends SpawnedEffect + @SerialVersionUID(1L) final case object SpawnedAdapter extends SpawnedEffect @SerialVersionUID(1L) final case class Stopped(childName: String) extends Effect @SerialVersionUID(1L) final case class Watched[T](other: ActorRef[T]) extends Effect @SerialVersionUID(1L) final case class Unwatched[T](other: ActorRef[T]) extends Effect @@ -78,18 +83,23 @@ class EffectfulActorContext[T](_name: String, _initialBehavior: Behavior[T], _ma } override def spawnAnonymous[U](behavior: Behavior[U], props: Props = Props.empty): ActorRef[U] = { - val ref = super.spawnAnonymous(behavior) - effectQueue.offer(Spawned(ref.path.name)) + val ref = super.spawnAnonymous(behavior, props) + effectQueue.offer(SpawnedAnonymous(props)) ref } - override def spawnAdapter[U](f: U ⇒ T, name: String = ""): ActorRef[U] = { + + override def spawnAdapter[U](f: U ⇒ T): ActorRef[U] = { + spawnAdapter(f, "") + } + + override def spawnAdapter[U](f: U ⇒ T, name: String): ActorRef[U] = { val ref = super.spawnAdapter(f, name) - effectQueue.offer(Spawned(ref.path.name)) + effectQueue.offer(SpawnedAdapter) ref } override def spawn[U](behavior: Behavior[U], name: String, props: Props = Props.empty): ActorRef[U] = { - effectQueue.offer(Spawned(name)) - super.spawn(behavior, name) + effectQueue.offer(Spawned(name, props)) + super.spawn(behavior, name, props) } override def stop[U](child: ActorRef[U]): Boolean = { effectQueue.offer(Stopped(child.path.name)) diff --git a/akka-typed-testkit/src/test/scala/akka/typed/testkit/EffectfulActorContextSpec.scala b/akka-typed-testkit/src/test/scala/akka/typed/testkit/EffectfulActorContextSpec.scala new file mode 100644 index 0000000000..9a6ce3585f --- /dev/null +++ b/akka-typed-testkit/src/test/scala/akka/typed/testkit/EffectfulActorContextSpec.scala @@ -0,0 +1,138 @@ +/** + * Copyright (C) 2014-2017 Lightbend Inc. + */ + +package akka.typed.testkit + +import akka.typed.scaladsl.Actor +import akka.typed.testkit.Effect.{ Spawned, SpawnedAdapter, SpawnedAnonymous } +import akka.typed.testkit.EffectfulActorContextSpec.Father +import akka.typed.testkit.EffectfulActorContextSpec.Father._ +import akka.typed.{ ActorSystem, Behavior, Props } +import org.scalatest.{ FlatSpec, Matchers } + +object EffectfulActorContextSpec { + object Father { + + case class Reproduce(times: Int) + + sealed trait Command + + case class SpawnChildren(numberOfChildren: Int) extends Command + case class SpawnChildrenWithProps(numberOfChildren: Int, props: Props) extends Command + case class SpawnAnonymous(numberOfChildren: Int) extends Command + case class SpawnAnonymousWithProps(numberOfChildren: Int, props: Props) extends Command + case object SpawnAdapter extends Command + case class SpawnAdapterWithName(name: String) extends Command + + def behavior: Behavior[Command] = init() + + def init(): Behavior[Command] = Actor.immutable[Command] { (ctx, msg) ⇒ + msg match { + case SpawnChildren(numberOfChildren) if numberOfChildren > 0 ⇒ + 0.until(numberOfChildren).foreach { i ⇒ + ctx.spawn(Child.initial, s"child$i") + } + Actor.same + case SpawnChildrenWithProps(numberOfChildren, props) if numberOfChildren > 0 ⇒ + 0.until(numberOfChildren).foreach { i ⇒ + ctx.spawn(Child.initial, s"child$i", props) + } + Actor.same + case SpawnAnonymous(numberOfChildren) if numberOfChildren > 0 ⇒ + 0.until(numberOfChildren).foreach { _ ⇒ + ctx.spawnAnonymous(Child.initial) + } + Actor.same + case SpawnAnonymousWithProps(numberOfChildren, props) if numberOfChildren > 0 ⇒ + 0.until(numberOfChildren).foreach { _ ⇒ + ctx.spawnAnonymous(Child.initial, props) + } + Actor.same + case SpawnAdapter ⇒ + ctx.spawnAdapter { + r: Reproduce ⇒ SpawnAnonymous(r.times) + } + Actor.same + case SpawnAdapterWithName(name) ⇒ + ctx.spawnAdapter({ + r: Reproduce ⇒ SpawnAnonymous(r.times) + }, name) + Actor.same + } + } + } + + object Child { + + sealed trait Action + + def initial: Behavior[Action] = Actor.immutable[Action] { (_, msg) ⇒ + msg match { + case _ ⇒ + Actor.empty + } + } + + } + +} + +class EffectfulActorContextSpec extends FlatSpec with Matchers { + + private val props = Props.empty.withMailboxCapacity(10) + + "EffectfulActorContext's spawn" should "create children when no props specified" in { + val system = ActorSystem.create(Father.init(), "father-system") + val ctx = new EffectfulActorContext[Father.Command]("father-test", Father.init(), 100, system) + + ctx.run(SpawnChildren(2)) + val effects = ctx.getAllEffects() + effects should contain only (Spawned("child0", Props.empty), Spawned("child1", Props.empty)) + } + + it should "create children when props specified and record effects" in { + val system = ActorSystem.create(Father.init(), "father-system") + val ctx = new EffectfulActorContext[Father.Command]("father-test", Father.init(), 100, system) + + ctx.run(SpawnChildrenWithProps(2, props)) + val effects = ctx.getAllEffects() + effects should contain only (Spawned("child0", props), Spawned("child1", props)) + } + + "EffectfulActorContext's spawnAnonymous" should "create children when no props specified and record effects" in { + val system = ActorSystem.create(Father.init(), "father-system") + val ctx = new EffectfulActorContext[Father.Command]("father-test", Father.init(), 100, system) + + ctx.run(SpawnAnonymous(2)) + val effects = ctx.getAllEffects() + effects shouldBe Seq(SpawnedAnonymous(Props.empty), SpawnedAnonymous(Props.empty)) + } + + it should "create children when props specified and record effects" in { + val system = ActorSystem.create(Father.init(), "father-system") + val ctx = new EffectfulActorContext[Father.Command]("father-test", Father.init(), 100, system) + + ctx.run(SpawnAnonymousWithProps(2, props)) + val effects = ctx.getAllEffects() + effects shouldBe Seq(SpawnedAnonymous(props), SpawnedAnonymous(props)) + } + + "EffectfulActorContext's spawnAdapter" should "create adapters without name and record effects" in { + val system = ActorSystem.create(Father.init(), "father-system") + val ctx = new EffectfulActorContext[Father.Command]("father-test", Father.init(), 100, system) + + ctx.run(SpawnAdapter) + val effects = ctx.getAllEffects() + effects shouldBe Seq(SpawnedAdapter) + } + + it should "create adapters with name and record effects" in { + val system = ActorSystem.create(Father.init(), "father-system") + val ctx = new EffectfulActorContext[Father.Command]("father-test", Father.init(), 100, system) + + ctx.run(SpawnAdapterWithName("adapter")) + val effects = ctx.getAllEffects() + effects shouldBe Seq(SpawnedAdapter) + } +}