diff --git a/supervisor/test-code/test/scala/GenericServerContainerSuite.scala b/supervisor/test-code/test/scala/GenericServerContainerSuite.scala
new file mode 100755
index 0000000000..1b85425d74
--- /dev/null
+++ b/supervisor/test-code/test/scala/GenericServerContainerSuite.scala
@@ -0,0 +1,202 @@
+/**
+ * Copyright (C) 2009 Scalable Solutions.
+ */
+
+package com.scalablesolutions.akka.supervisor
+
+import org.specs.runner.JUnit4
+import org.specs.Specification
+
+import scala.actors._
+import scala.actors.Actor._
+
+/**
+ * @author Jonas Bonér
+ */
+class GenericServerContainerTest extends JUnit4(genericServerContainerSpec) // for JUnit4 and Maven
+object genericServerContainerSpec extends Specification {
+
+ var inner: GenericServerContainerActor = null
+ var server: GenericServerContainer = null
+ def createProxy(f: () => GenericServer) = {
+ val server = new GenericServerContainer("server", f)
+ server.setTimeout(100)
+ server
+ }
+
+ inner = new GenericServerContainerActor
+ server = createProxy(() => inner)
+ server.newServer
+ server.start
+
+ "server should be initialized" in {
+ server.init("testInit")
+ Thread.sleep(100)
+ expect("initializing: testInit") {
+ inner.log
+ }
+ }
+
+ "server should terminate with a reason " in {
+ server.terminate("testTerminateWithReason", 100)
+ Thread.sleep(100)
+ expect("terminating: testTerminateWithReason") {
+ inner.log
+ }
+ }
+
+ "server respond to async oneway message" in {
+ server ! OneWay
+ Thread.sleep(100)
+ expect("got a oneway") {
+ inner.log
+ }
+ }
+
+ "server respond to async ping message" in {
+ server ! Ping
+ Thread.sleep(100)
+ expect("got a ping") {
+ inner.log
+ }
+ }
+
+ "server respond to !!!" in {
+ expect("pong") {
+ (server !!! Ping).getOrElse("nil")
+ }
+ expect("got a ping") {
+ inner.log
+ }
+ }
+
+ "server respond to !?" in {
+ expect("pong") {
+ val res: String = server !? Ping
+ res
+ }
+ expect("got a ping") {
+ inner.log
+ }
+ }
+
+ "server respond to !!! with timeout" in {
+ expect("pong") {
+ (server !!! Ping).getOrElse("nil")
+ }
+ expect("got a ping") {
+ inner.log
+ }
+ }
+
+ "server respond to !!! with timeout" in {
+ expect("error handler") {
+ server !!! (OneWay, "error handler")
+ }
+ expect("got a oneway") {
+ inner.log
+ }
+ }
+
+ "server respond to !!! and return future with timeout" in {
+ val future = server !! Ping
+ future.receiveWithin(100) match {
+ case None => fail("timed out") // timed out
+ case Some(reply) =>
+ expect("got a ping") {
+ inner.log
+ }
+ assert("pong" === reply)
+ }
+ }
+
+ "server respond to !!! and return future with timeout" in {
+ val future = server !! OneWay
+ future.receiveWithin(100) match {
+ case None =>
+ expect("got a oneway") {
+ inner.log
+ }
+ case Some(reply) =>
+ fail("expected a timeout, got Some(reply)")
+ }
+ }
+
+ "server respond do hotswap" in {
+ // using base
+ expect("pong") {
+ (server !!! Ping).getOrElse("nil")
+ }
+
+ // hotswapping
+ server.hotswap(Some({
+ case Ping => reply("hotswapped pong")
+ }))
+ expect("hotswapped pong") {
+ (server !!! Ping).getOrElse("nil")
+ }
+ }
+
+ "server respond do double hotswap" in {
+ // using base
+ expect("pong") {
+ (server !!! Ping).getOrElse("nil")
+ }
+
+ // hotswapping
+ server.hotswap(Some({
+ case Ping => reply("hotswapped pong")
+ }))
+ expect("hotswapped pong") {
+ (server !!! Ping).getOrElse("nil")
+ }
+
+ // hotswapping again
+ server.hotswap(Some({
+ case Ping => reply("hotswapped pong again")
+ }))
+ expect("hotswapped pong again") {
+ (server !!! Ping).getOrElse("nil")
+ }
+ }
+
+ "server respond do hotswap and then revert" in {
+ // using base
+ expect("pong") {
+ (server !!! Ping).getOrElse("nil")
+ }
+
+ // hotswapping
+ server.hotswap(Some({
+ case Ping => reply("hotswapped pong")
+ }))
+ expect("hotswapped pong") {
+ (server !!! Ping).getOrElse("nil")
+ }
+
+ // restoring original base
+ server.hotswap(None)
+ expect("pong") {
+ (server !!! Ping).getOrElse("nil")
+ }
+ }
+}
+
+
+class GenericServerContainerActor extends GenericServer {
+ var log = ""
+
+ override def body: PartialFunction[Any, Unit] = {
+ case Ping =>
+ log = "got a ping"
+ reply("pong")
+
+ case OneWay =>
+ log = "got a oneway"
+ }
+
+ override def init(config: AnyRef) = log = "initializing: " + config
+ override def shutdown(reason: AnyRef) = log = "terminating: " + reason
+}
+
+
diff --git a/supervisor/test-code/test/scala/GenericServerSuite.scala b/supervisor/test-code/test/scala/GenericServerSuite.scala
new file mode 100755
index 0000000000..44aab326eb
--- /dev/null
+++ b/supervisor/test-code/test/scala/GenericServerSuite.scala
@@ -0,0 +1,40 @@
+/**
+ * Copyright (C) 2009 Scalable Solutions.
+ */
+
+package com.scalablesolutions.akka.supervisor
+
+import org.specs.runner.JUnit4
+import org.specs.Specification
+
+import scala.actors._
+import scala.actors.Actor._
+
+/**
+ * @author Jonas Bonér
+ */
+class GenericServerTest extends JUnit4(genericServerSpec) // for JUnit4 and Maven
+object genericServerSpec extends Specification {
+
+ "server should respond to a regular message" in {
+ val server = new TestGenericServerActor
+ server.start
+ server !? Ping match {
+ case reply: String =>
+ assert("got a ping" === server.log)
+ assert("pong" === reply)
+ case _ => fail()
+ }
+ }
+}
+
+class TestGenericServerActor extends GenericServer {
+ var log: String = ""
+
+ override def body: PartialFunction[Any, Unit] = {
+ case Ping =>
+ log = "got a ping"
+ reply("pong")
+ }
+}
+
diff --git a/supervisor/test-code/test/scala/Messages.scala b/supervisor/test-code/test/scala/Messages.scala
new file mode 100755
index 0000000000..0f014ca692
--- /dev/null
+++ b/supervisor/test-code/test/scala/Messages.scala
@@ -0,0 +1,12 @@
+/**
+ * Copyright (C) 2009 Scalable Solutions.
+ */
+
+package com.scalablesolutions.akka.supervisor
+
+sealed abstract class TestMessage
+case object Ping extends TestMessage
+case object Pong extends TestMessage
+case object OneWay extends TestMessage
+case object Die extends TestMessage
+case object NotifySupervisorExit extends TestMessage
diff --git a/supervisor/test-code/test/scala/SupervisorStateSuite.scala b/supervisor/test-code/test/scala/SupervisorStateSuite.scala
new file mode 100755
index 0000000000..6df3b7e059
--- /dev/null
+++ b/supervisor/test-code/test/scala/SupervisorStateSuite.scala
@@ -0,0 +1,77 @@
+/**
+ * Copyright (C) 2009 Scalable Solutions.
+ */
+
+package com.scalablesolutions.akka.supervisor
+
+import org.specs.runner.JUnit4
+import org.specs.Specification
+
+import scala.actors._
+import scala.actors.Actor._
+
+/**
+ * @author Jonas Bonér
+ */
+class SupervisorStateTest extends JUnit4(supervisorStateSpec) // for JUnit4 and Maven
+object supervisorStateSpec extends Specification {
+ val dummyActor = new GenericServer { override def body: PartialFunction[Any, Unit] = { case _ => }}
+ val newDummyActor = () => dummyActor
+ var state: SupervisorState = _
+ var proxy: GenericServerContainer = _
+ var supervisor: Supervisor = _
+
+ proxy = new GenericServerContainer("server1", newDummyActor)
+ object factory extends SupervisorFactory {
+ override def getSupervisorConfig: SupervisorConfig = {
+ SupervisorConfig(
+ RestartStrategy(AllForOne, 3, 100),
+ Worker(
+ proxy,
+ LifeCycle(Permanent, 100))
+ :: Nil)
+ }
+ }
+
+ supervisor = factory.newSupervisor
+ state = new SupervisorState(supervisor, new AllForOneStrategy(3, 100))
+
+ "supervisor state should return added server" in {
+ state.addServerContainer(proxy)
+ state.getServerContainer("server1") match {
+ case None => fail("should have returned server")
+ case Some(server) =>
+ assert(server != null)
+ assert(server.isInstanceOf[GenericServerContainer])
+ assert(proxy === server)
+ }
+ }
+
+ "supervisor state should remove added server" in {
+ state.addServerContainer(proxy)
+
+ state.removeServerContainer("server1")
+ state.getServerContainer("server1") match {
+ case Some(_) => fail("should have returned None")
+ case None =>
+ }
+ state.getServerContainer("dummyActor") match {
+ case Some(_) => fail("should have returned None")
+ case None =>
+ }
+ }
+
+ "supervisor state should fail getting non-existent server by symbol" in {
+ state.getServerContainer("server2") match {
+ case Some(_) => fail("should have returned None")
+ case None =>
+ }
+ }
+
+ "supervisor state should fail getting non-existent server by actor" in {
+ state.getServerContainer("dummyActor") match {
+ case Some(_) => fail("should have returned None")
+ case None =>
+ }
+ }
+}
diff --git a/supervisor/test-code/test/scala/SupervisorSuite.scala b/supervisor/test-code/test/scala/SupervisorSuite.scala
new file mode 100755
index 0000000000..4e8bd048e1
--- /dev/null
+++ b/supervisor/test-code/test/scala/SupervisorSuite.scala
@@ -0,0 +1,434 @@
+/**
+ * Copyright (C) 2009 Scalable Solutions.
+ */
+
+package com.scalablesolutions.akka.supervisor
+
+import org.specs.runner.JUnit4
+import org.specs.Specification
+
+import scala.actors._
+import scala.actors.Actor._
+import scala.collection.Map
+import scala.collection.mutable.HashMap
+
+/**
+ * @author Jonas Bonér
+ */
+class SupervisorTest extends JUnit4(supervisorSpec) // for JUnit4 and Maven
+object supervisorSpec extends Specification {
+
+ var messageLog: String = ""
+ val pingpong1 = new GenericServerContainer("pingpong1", () => new PingPong1Actor)
+ val pingpong2 = new GenericServerContainer("pingpong2", () => new PingPong2Actor)
+ val pingpong3 = new GenericServerContainer("pingpong3", () => new PingPong3Actor)
+
+ pingpong1.setTimeout(100)
+ pingpong2.setTimeout(100)
+ pingpong3.setTimeout(100)
+
+ @BeforeMethod
+ def setup = messageLog = ""
+
+ // ===========================================
+ "starting supervisor should start the servers" in {
+ val sup = getSingleActorAllForOneSupervisor
+ sup ! Start
+
+ expect("pong") {
+ (pingpong1 !!! Ping).getOrElse("nil")
+ }
+ }
+
+ // ===========================================
+ "started supervisor should be able to return started servers" in {
+ val sup = getSingleActorAllForOneSupervisor
+ sup ! Start
+ val server = sup.getServerOrElse("pingpong1", throw new RuntimeException("server not found"))
+ assert(server.isInstanceOf[GenericServerContainer])
+ assert(server === pingpong1)
+ }
+
+ // ===========================================
+ "started supervisor should fail returning non-existing server" in {
+ val sup = getSingleActorAllForOneSupervisor
+ sup ! Start
+ intercept(classOf[RuntimeException]) {
+ sup.getServerOrElse("wrong_name", throw new RuntimeException("server not found"))
+ }
+ }
+
+ // ===========================================
+ "supervisor should restart killed server with restart strategy one_for_one" in {
+ val sup = getSingleActorOneForOneSupervisor
+ sup ! Start
+
+ intercept(classOf[RuntimeException]) {
+ pingpong1 !!! (Die, throw new RuntimeException("TIME OUT"))
+ }
+ Thread.sleep(100)
+ expect("oneforone") {
+ messageLog
+ }
+ }
+
+ // ===========================================
+ "supervisor should restart used killed server with restart strategy one_for_one" in {
+ val sup = getSingleActorOneForOneSupervisor
+ sup ! Start
+
+ expect("pong") {
+ (pingpong1 !!! Ping).getOrElse("nil")
+ }
+ Thread.sleep(100)
+ expect("ping") {
+ messageLog
+ }
+ intercept(classOf[RuntimeException]) {
+ pingpong1 !!! (Die, throw new RuntimeException("TIME OUT"))
+ }
+ Thread.sleep(100)
+ expect("pingoneforone") {
+ messageLog
+ }
+ expect("pong") {
+ (pingpong1 !!! Ping).getOrElse("nil")
+ }
+ Thread.sleep(100)
+ expect("pingoneforoneping") {
+ messageLog
+ }
+ }
+
+ // ===========================================
+ "supervisor should restart killed server with restart strategy all_for_one" in {
+ val sup = getSingleActorAllForOneSupervisor
+ sup ! Start
+ intercept(classOf[RuntimeException]) {
+ pingpong1 !!! (Die, throw new RuntimeException("TIME OUT"))
+ }
+ Thread.sleep(100)
+ expect("allforone") {
+ messageLog
+ }
+ }
+
+ // ===========================================
+ "supervisor should restart used killed server with restart strategy all_for_one" in {
+ val sup = getSingleActorAllForOneSupervisor
+ sup ! Start
+ expect("pong") {
+ (pingpong1 !!! Ping).getOrElse("nil")
+ }
+ Thread.sleep(100)
+ expect("ping") {
+ messageLog
+ }
+ intercept(classOf[RuntimeException]) {
+ pingpong1 !!! (Die, throw new RuntimeException("TIME OUT"))
+ }
+ Thread.sleep(100)
+ expect("pingallforone") {
+ messageLog
+ }
+ expect("pong") {
+ (pingpong1 !!! Ping).getOrElse("nil")
+ }
+ Thread.sleep(100)
+ expect("pingallforoneping") {
+ messageLog
+ }
+ }
+
+ // ===========================================
+ "supervisor should restart killed multiple servers with restart strategy one_for_one" in {
+ val sup = getMultipleActorsOneForOneConf
+ sup ! Start
+ intercept(classOf[RuntimeException]) {
+ pingpong3 !!! (Die, throw new RuntimeException("TIME OUT"))
+ }
+ Thread.sleep(100)
+ expect("oneforone") {
+ messageLog
+ }
+ }
+
+ // ===========================================
+ "supervisor should restart killed multiple servers with restart strategy one_for_one" in {
+ val sup = getMultipleActorsOneForOneConf
+ sup ! Start
+ expect("pong") {
+ (pingpong1 !!! Ping).getOrElse("nil")
+ }
+ Thread.sleep(100)
+ expect("pong") {
+ (pingpong2 !!! Ping).getOrElse("nil")
+ }
+ Thread.sleep(100)
+ expect("pong") {
+ (pingpong3 !!! Ping).getOrElse("nil")
+ }
+ Thread.sleep(100)
+ expect("pingpingping") {
+ messageLog
+ }
+ intercept(classOf[RuntimeException]) {
+ pingpong2 !!! (Die, throw new RuntimeException("TIME OUT"))
+ }
+ Thread.sleep(100)
+ expect("pingpingpingoneforone") {
+ messageLog
+ }
+ expect("pong") {
+ (pingpong1 !!! Ping).getOrElse("nil")
+ }
+ Thread.sleep(100)
+ expect("pong") {
+ (pingpong2 !!! Ping).getOrElse("nil")
+ }
+ Thread.sleep(100)
+ expect("pong") {
+ (pingpong3 !!! Ping).getOrElse("nil")
+ }
+ Thread.sleep(100)
+ expect("pingpingpingoneforonepingpingping") {
+ messageLog
+ }
+ }
+
+ // ===========================================
+ "supervisor should restart killed muliple servers with restart strategy all_for_one" in {
+ val sup = getMultipleActorsAllForOneConf
+ sup ! Start
+ intercept(classOf[RuntimeException]) {
+ pingpong2 !!! (Die, throw new RuntimeException("TIME OUT"))
+ }
+ Thread.sleep(100)
+ expect("allforoneallforoneallforone") {
+ messageLog
+ }
+ }
+
+ // ===========================================
+ "supervisor should restart killed muliple servers with restart strategy all_for_one" in {
+ val sup = getMultipleActorsAllForOneConf
+ sup ! Start
+ expect("pong") {
+ (pingpong1 !!! Ping).getOrElse("nil")
+ }
+ Thread.sleep(100)
+ expect("pong") {
+ (pingpong2 !!! Ping).getOrElse("nil")
+ }
+ Thread.sleep(100)
+ expect("pong") {
+ (pingpong3 !!! Ping).getOrElse("nil")
+ }
+ Thread.sleep(100)
+ expect("pingpingping") {
+ messageLog
+ }
+ intercept(classOf[RuntimeException]) {
+ pingpong2 !!! (Die, throw new RuntimeException("TIME OUT"))
+ }
+ Thread.sleep(100)
+ expect("pingpingpingallforoneallforoneallforone") {
+ messageLog
+ }
+ expect("pong") {
+ (pingpong1 !!! Ping).getOrElse("nil")
+ }
+ Thread.sleep(100)
+ expect("pong") {
+ (pingpong2 !!! Ping).getOrElse("nil")
+ }
+ Thread.sleep(100)
+ expect("pong") {
+ (pingpong3 !!! Ping).getOrElse("nil")
+ }
+ Thread.sleep(100)
+ expect("pingpingpingallforoneallforoneallforonepingpingping") {
+ messageLog
+ }
+ }
+
+ "supervisor should restart killed first-level server with restart strategy all_for_one" in {
+ val sup = getNestedSupervisorsAllForOneConf
+ sup ! Start
+ intercept(classOf[RuntimeException]) {
+ pingpong1 !!! (Die, throw new RuntimeException("TIME OUT"))
+ }
+ Thread.sleep(100)
+ expect("allforoneallforoneallforone") {
+ messageLog
+ }
+ }
+
+
+ // =============================================
+ // Creat some supervisors with different configurations
+
+ def getSingleActorAllForOneSupervisor: Supervisor = {
+
+ // Create an abstract SupervisorContainer that works for all implementations
+ // of the different Actors (Services).
+ //
+ // Then create a concrete container in which we mix in support for the specific
+ // implementation of the Actors we want to use.
+
+ object factory extends TestSupervisorFactory {
+ override def getSupervisorConfig: SupervisorConfig = {
+ SupervisorConfig(
+ RestartStrategy(AllForOne, 3, 100),
+ Worker(
+ pingpong1,
+ LifeCycle(Permanent, 100))
+ :: Nil)
+ }
+ }
+ factory.newSupervisor
+ }
+
+ def getSingleActorOneForOneSupervisor: Supervisor = {
+ object factory extends TestSupervisorFactory {
+ override def getSupervisorConfig: SupervisorConfig = {
+ SupervisorConfig(
+ RestartStrategy(OneForOne, 3, 100),
+ Worker(
+ pingpong1,
+ LifeCycle(Permanent, 100))
+ :: Nil)
+ }
+ }
+ factory.newSupervisor
+ }
+
+ def getMultipleActorsAllForOneConf: Supervisor = {
+ object factory extends TestSupervisorFactory {
+ override def getSupervisorConfig: SupervisorConfig = {
+ SupervisorConfig(
+ RestartStrategy(AllForOne, 3, 100),
+ Worker(
+ pingpong1,
+ LifeCycle(Permanent, 100))
+ ::
+ Worker(
+ pingpong2,
+ LifeCycle(Permanent, 100))
+ ::
+ Worker(
+ pingpong3,
+ LifeCycle(Permanent, 100))
+ :: Nil)
+ }
+ }
+ factory.newSupervisor
+ }
+
+ def getMultipleActorsOneForOneConf: Supervisor = {
+ object factory extends TestSupervisorFactory {
+ override def getSupervisorConfig: SupervisorConfig = {
+ SupervisorConfig(
+ RestartStrategy(OneForOne, 3, 100),
+ Worker(
+ pingpong1,
+ LifeCycle(Permanent, 100))
+ ::
+ Worker(
+ pingpong2,
+ LifeCycle(Permanent, 100))
+ ::
+ Worker(
+ pingpong3,
+ LifeCycle(Permanent, 100))
+ :: Nil)
+ }
+ }
+ factory.newSupervisor
+ }
+
+ def getNestedSupervisorsAllForOneConf: Supervisor = {
+ object factory extends TestSupervisorFactory {
+ override def getSupervisorConfig: SupervisorConfig = {
+ SupervisorConfig(
+ RestartStrategy(AllForOne, 3, 100),
+ Worker(
+ pingpong1,
+ LifeCycle(Permanent, 100))
+ ::
+ SupervisorConfig(
+ RestartStrategy(AllForOne, 3, 100),
+ Worker(
+ pingpong2,
+ LifeCycle(Permanent, 100))
+ ::
+ Worker(
+ pingpong3,
+ LifeCycle(Permanent, 100))
+ :: Nil)
+ :: Nil)
+ }
+ }
+ factory.newSupervisor
+ }
+
+ class PingPong1Actor extends GenericServer {
+ override def body: PartialFunction[Any, Unit] = {
+ case Ping =>
+ messageLog += "ping"
+ reply("pong")
+ case Die =>
+ throw new RuntimeException("Recieved Die message")
+ }
+ }
+
+ class PingPong2Actor extends GenericServer {
+ override def body: PartialFunction[Any, Unit] = {
+ case Ping =>
+ messageLog += "ping"
+ reply("pong")
+ case Die =>
+ throw new RuntimeException("Recieved Die message")
+ }
+ }
+
+ class PingPong3Actor extends GenericServer {
+ override def body: PartialFunction[Any, Unit] = {
+ case Ping =>
+ messageLog += "ping"
+ reply("pong")
+ case Die =>
+ throw new RuntimeException("Recieved Die message")
+ }
+ }
+
+ // =============================================
+
+ class TestAllForOneStrategy(maxNrOfRetries: Int, withinTimeRange: Int) extends AllForOneStrategy(maxNrOfRetries, withinTimeRange) {
+ override def postRestart(serverContainer: GenericServerContainer) = {
+ messageLog += "allforone"
+ }
+ }
+
+ class TestOneForOneStrategy(maxNrOfRetries: Int, withinTimeRange: Int) extends OneForOneStrategy(maxNrOfRetries, withinTimeRange) {
+ override def postRestart(serverContainer: GenericServerContainer) = {
+ messageLog += "oneforone"
+ }
+ }
+
+ abstract class TestSupervisorFactory extends SupervisorFactory {
+ override def create(strategy: RestartStrategy): Supervisor = strategy match {
+ case RestartStrategy(scheme, maxNrOfRetries, timeRange) =>
+ scheme match {
+ case AllForOne => new Supervisor(new TestAllForOneStrategy(maxNrOfRetries, timeRange))
+ case OneForOne => new Supervisor(new TestOneForOneStrategy(maxNrOfRetries, timeRange))
+ }
+ }
+ }
+}
+
+
+
+
+
+