2011-04-27 15:00:41 +02:00
|
|
|
/**
|
|
|
|
|
* Copyright (C) 2009-2011 Scalable Solutions AB <http://scalablesolutions.se>
|
|
|
|
|
*/
|
|
|
|
|
|
2011-04-16 22:20:04 +02:00
|
|
|
package akka.testkit
|
|
|
|
|
|
|
|
|
|
import akka.actor._
|
|
|
|
|
import akka.util.ReflectiveAccess
|
|
|
|
|
import akka.event.EventHandler
|
|
|
|
|
|
2011-04-27 15:00:41 +02:00
|
|
|
import com.eaio.uuid.UUID
|
|
|
|
|
|
2011-04-16 22:20:04 +02:00
|
|
|
/**
|
|
|
|
|
* This special ActorRef is exclusively for use during unit testing in a single-threaded environment. Therefore, it
|
|
|
|
|
* overrides the dispatcher to CallingThreadDispatcher and sets the receiveTimeout to None. Otherwise,
|
|
|
|
|
* it acts just like a normal ActorRef. You may retrieve a reference to the underlying actor to test internal logic.
|
|
|
|
|
*
|
|
|
|
|
* @author Roland Kuhn
|
|
|
|
|
* @since 1.1
|
|
|
|
|
*/
|
2011-07-08 08:28:13 +02:00
|
|
|
class TestActorRef[T <: Actor](factory: () ⇒ T, address: String) extends LocalActorRef(factory, address) {
|
2011-04-16 22:20:04 +02:00
|
|
|
|
|
|
|
|
dispatcher = CallingThreadDispatcher.global
|
|
|
|
|
receiveTimeout = None
|
|
|
|
|
|
2011-04-21 21:31:24 +02:00
|
|
|
/**
|
|
|
|
|
* Directly inject messages into actor receive behavior. Any exceptions
|
|
|
|
|
* thrown will be available to you, while still being able to use
|
|
|
|
|
* become/unbecome and their message counterparts.
|
|
|
|
|
*/
|
2011-05-18 17:25:30 +02:00
|
|
|
def apply(o: Any) { actor(o) }
|
2011-04-21 21:31:24 +02:00
|
|
|
|
2011-04-16 22:20:04 +02:00
|
|
|
/**
|
|
|
|
|
* Retrieve reference to the underlying actor, where the static type matches the factory used inside the
|
|
|
|
|
* constructor. Beware that this reference is discarded by the ActorRef upon restarting the actor (should this
|
|
|
|
|
* reference be linked to a supervisor). The old Actor may of course still be used in post-mortem assertions.
|
|
|
|
|
*/
|
2011-05-18 17:25:30 +02:00
|
|
|
def underlyingActor: T = actor.asInstanceOf[T]
|
2011-04-16 22:20:04 +02:00
|
|
|
|
2011-04-27 15:00:41 +02:00
|
|
|
override def toString = "TestActor[" + address + ":" + uuid + "]"
|
2011-04-16 22:20:04 +02:00
|
|
|
|
2011-05-18 17:25:30 +02:00
|
|
|
override def equals(other: Any) =
|
2011-04-16 22:20:04 +02:00
|
|
|
other.isInstanceOf[TestActorRef[_]] &&
|
2011-05-18 17:25:30 +02:00
|
|
|
other.asInstanceOf[TestActorRef[_]].uuid == uuid
|
2011-04-16 22:20:04 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Override to check whether the new supervisor is running on the CallingThreadDispatcher,
|
|
|
|
|
* as it should be. This can of course be tricked by linking before setting the dispatcher before starting the
|
|
|
|
|
* supervisor, but then you just asked for trouble.
|
|
|
|
|
*/
|
2011-05-18 17:25:30 +02:00
|
|
|
override def supervisor_=(a: Option[ActorRef]) {
|
|
|
|
|
for (ref ← a) {
|
2011-04-16 22:20:04 +02:00
|
|
|
if (!ref.dispatcher.isInstanceOf[CallingThreadDispatcher])
|
2011-05-18 17:25:30 +02:00
|
|
|
EventHandler.warning(this, "supervisor " + ref + " does not use CallingThreadDispatcher")
|
2011-04-16 22:20:04 +02:00
|
|
|
}
|
|
|
|
|
super.supervisor_=(a)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
object TestActorRef {
|
|
|
|
|
|
2011-05-18 17:25:30 +02:00
|
|
|
def apply[T <: Actor](factory: ⇒ T): TestActorRef[T] = apply[T](factory, new UUID().toString)
|
2011-04-27 15:00:41 +02:00
|
|
|
|
2011-05-18 17:25:30 +02:00
|
|
|
def apply[T <: Actor](factory: ⇒ T, address: String): TestActorRef[T] = new TestActorRef(() ⇒ factory, address)
|
2011-04-27 15:00:41 +02:00
|
|
|
|
2011-05-18 17:25:30 +02:00
|
|
|
def apply[T <: Actor: Manifest]: TestActorRef[T] = apply[T](new UUID().toString)
|
2011-04-27 15:00:41 +02:00
|
|
|
|
2011-05-18 17:25:30 +02:00
|
|
|
def apply[T <: Actor: Manifest](address: String): TestActorRef[T] = new TestActorRef[T]({ () ⇒
|
2011-04-16 22:20:04 +02:00
|
|
|
|
|
|
|
|
import ReflectiveAccess.{ createInstance, noParams, noArgs }
|
2011-04-29 17:15:00 +02:00
|
|
|
createInstance[T](manifest[T].erasure, noParams, noArgs) match {
|
2011-05-18 17:25:30 +02:00
|
|
|
case r: Right[_, T] ⇒ r.b
|
|
|
|
|
case l: Left[Exception, _] ⇒ throw new ActorInitializationException(
|
2011-04-16 22:20:04 +02:00
|
|
|
"Could not instantiate Actor" +
|
2011-05-18 17:25:30 +02:00
|
|
|
"\nMake sure Actor is NOT defined inside a class/trait," +
|
|
|
|
|
"\nif so put it outside the class/trait, f.e. in a companion object," +
|
|
|
|
|
"\nOR try to change: 'actorOf[MyActor]' to 'actorOf(new MyActor)'.", l.a)
|
2011-04-29 17:15:00 +02:00
|
|
|
}
|
2011-04-27 15:00:41 +02:00
|
|
|
}, address)
|
2011-04-21 21:31:24 +02:00
|
|
|
}
|