2011-04-27 15:00:41 +02:00
|
|
|
/**
|
2012-01-19 18:21:06 +01:00
|
|
|
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
2011-04-27 15:00:41 +02:00
|
|
|
*/
|
|
|
|
|
|
2011-04-16 22:20:04 +02:00
|
|
|
package akka.testkit
|
|
|
|
|
|
|
|
|
|
import akka.actor._
|
2011-12-10 16:56:53 +01:00
|
|
|
import akka.util.{ ReflectiveAccess, Duration }
|
2011-11-13 20:38:14 +01:00
|
|
|
import java.util.concurrent.atomic.AtomicLong
|
2011-12-10 16:56:53 +01:00
|
|
|
import scala.collection.immutable.Stack
|
2011-12-14 17:26:18 +01:00
|
|
|
import akka.dispatch._
|
2012-01-18 10:18:51 +01:00
|
|
|
import akka.pattern.ask
|
2011-04-27 15:00:41 +02:00
|
|
|
|
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-11-14 16:03:26 +01:00
|
|
|
class TestActorRef[T <: Actor](
|
2011-11-17 12:36:35 +01:00
|
|
|
_system: ActorSystemImpl,
|
2011-11-17 16:09:18 +01:00
|
|
|
_prerequisites: DispatcherPrerequisites,
|
2011-11-14 16:03:26 +01:00
|
|
|
_props: Props,
|
2011-12-02 14:41:13 +01:00
|
|
|
_supervisor: InternalActorRef,
|
2011-11-14 16:03:26 +01:00
|
|
|
name: String)
|
2011-12-10 16:56:53 +01:00
|
|
|
extends LocalActorRef(
|
|
|
|
|
_system,
|
2012-01-20 18:34:09 +01:00
|
|
|
_props.withDispatcher(
|
|
|
|
|
if (_props.dispatcher == Dispatchers.DefaultDispatcherId) CallingThreadDispatcher.Id
|
|
|
|
|
else _props.dispatcher),
|
2011-12-10 16:56:53 +01:00
|
|
|
_supervisor,
|
|
|
|
|
_supervisor.path / name,
|
|
|
|
|
false) {
|
|
|
|
|
|
|
|
|
|
private case object InternalGetActor extends AutoReceivedMessage
|
|
|
|
|
|
|
|
|
|
override def newActorCell(
|
|
|
|
|
system: ActorSystemImpl,
|
|
|
|
|
ref: InternalActorRef,
|
|
|
|
|
props: Props,
|
|
|
|
|
supervisor: InternalActorRef,
|
2012-01-30 13:27:48 +01:00
|
|
|
receiveTimeout: Option[Duration]): ActorCell =
|
|
|
|
|
new ActorCell(system, ref, props, supervisor, receiveTimeout) {
|
2011-12-10 16:56:53 +01:00
|
|
|
override def autoReceiveMessage(msg: Envelope) {
|
|
|
|
|
msg.message match {
|
|
|
|
|
case InternalGetActor ⇒ sender ! actor
|
|
|
|
|
case _ ⇒ super.autoReceiveMessage(msg)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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
|
2011-12-10 16:56:53 +01:00
|
|
|
* become/unbecome.
|
2011-04-21 21:31:24 +02:00
|
|
|
*/
|
2012-01-19 15:49:44 +01:00
|
|
|
def receive(o: Any) { underlyingActor.apply(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-12-10 16:56:53 +01:00
|
|
|
def underlyingActor: T = {
|
|
|
|
|
// volatile mailbox read to bring in actor field
|
|
|
|
|
if (isTerminated) throw new IllegalActorStateException("underlying actor is terminated")
|
|
|
|
|
underlying.actor.asInstanceOf[T] match {
|
2011-12-14 17:26:18 +01:00
|
|
|
case null ⇒
|
|
|
|
|
val t = underlying.system.settings.ActorTimeout
|
2011-12-17 17:18:50 -08:00
|
|
|
Await.result(this.?(InternalGetActor)(t), t.duration).asInstanceOf[T]
|
2011-12-14 17:26:18 +01:00
|
|
|
case ref ⇒ ref
|
2011-12-10 16:56:53 +01:00
|
|
|
}
|
|
|
|
|
}
|
2011-04-16 22:20:04 +02:00
|
|
|
|
2011-12-02 01:00:55 +01:00
|
|
|
/**
|
|
|
|
|
* Registers this actor to be a death monitor of the provided ActorRef
|
|
|
|
|
* This means that this actor will get a Terminated()-message when the provided actor
|
|
|
|
|
* is permanently terminated.
|
|
|
|
|
*
|
|
|
|
|
* @return the same ActorRef that is provided to it, to allow for cleaner invocations
|
|
|
|
|
*/
|
2011-12-06 09:59:52 +01:00
|
|
|
def watch(subject: ActorRef): ActorRef = underlying.watch(subject)
|
2011-12-02 01:00:55 +01:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Deregisters this actor from being a death monitor of the provided ActorRef
|
|
|
|
|
* This means that this actor will not get a Terminated()-message when the provided actor
|
|
|
|
|
* is permanently terminated.
|
|
|
|
|
*
|
|
|
|
|
* @return the same ActorRef that is provided to it, to allow for cleaner invocations
|
|
|
|
|
*/
|
2011-12-06 09:59:52 +01:00
|
|
|
def unwatch(subject: ActorRef): ActorRef = underlying.unwatch(subject)
|
2011-12-02 01:00:55 +01:00
|
|
|
|
2011-11-30 23:30:56 +01:00
|
|
|
override def toString = "TestActor[" + path + "]"
|
2011-04-16 22:20:04 +02:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
object TestActorRef {
|
|
|
|
|
|
2011-11-13 20:38:14 +01:00
|
|
|
private val number = new AtomicLong
|
|
|
|
|
private[testkit] def randomName: String = {
|
|
|
|
|
val l = number.getAndIncrement()
|
|
|
|
|
"$" + akka.util.Helpers.base64(l)
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-17 12:36:35 +01:00
|
|
|
def apply[T <: Actor](factory: ⇒ T)(implicit system: ActorSystem): TestActorRef[T] = apply[T](Props(factory), randomName)
|
2011-04-27 15:00:41 +02:00
|
|
|
|
2011-11-17 12:36:35 +01:00
|
|
|
def apply[T <: Actor](factory: ⇒ T, name: String)(implicit system: ActorSystem): TestActorRef[T] = apply[T](Props(factory), name)
|
2011-04-27 15:00:41 +02:00
|
|
|
|
2011-11-17 12:36:35 +01:00
|
|
|
def apply[T <: Actor](props: Props)(implicit system: ActorSystem): TestActorRef[T] = apply[T](props, randomName)
|
2011-04-27 15:00:41 +02:00
|
|
|
|
2011-11-17 12:36:35 +01:00
|
|
|
def apply[T <: Actor](props: Props, name: String)(implicit system: ActorSystem): TestActorRef[T] =
|
|
|
|
|
apply[T](props, system.asInstanceOf[ActorSystemImpl].guardian, name)
|
2011-10-18 15:39:26 +02:00
|
|
|
|
2011-11-17 16:09:18 +01:00
|
|
|
def apply[T <: Actor](props: Props, supervisor: ActorRef, name: String)(implicit system: ActorSystem): TestActorRef[T] =
|
2011-12-21 19:07:54 +01:00
|
|
|
new TestActorRef(system.asInstanceOf[ActorSystemImpl], system.dispatchers.prerequisites, props, supervisor.asInstanceOf[InternalActorRef], name)
|
2011-08-26 17:25:18 +02:00
|
|
|
|
2011-11-17 12:36:35 +01:00
|
|
|
def apply[T <: Actor](implicit m: Manifest[T], system: ActorSystem): TestActorRef[T] = apply[T](randomName)
|
2011-04-16 22:20:04 +02:00
|
|
|
|
2011-11-17 12:36:35 +01:00
|
|
|
def apply[T <: Actor](name: String)(implicit m: Manifest[T], system: ActorSystem): TestActorRef[T] = apply[T](Props({
|
2011-04-16 22:20:04 +02:00
|
|
|
import ReflectiveAccess.{ createInstance, noParams, noArgs }
|
2011-10-06 21:19:46 +02:00
|
|
|
createInstance[T](m.erasure, noParams, noArgs) match {
|
2011-07-19 19:28:46 +02:00
|
|
|
case Right(value) ⇒ value
|
2011-10-20 23:37:54 +02:00
|
|
|
case Left(exception) ⇒ throw new ActorInitializationException(null,
|
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," +
|
2011-12-13 14:09:40 +01:00
|
|
|
"\nOR try to change: 'actorOf(Props[MyActor]' to 'actorOf(Props(new MyActor)'.", exception)
|
2011-04-29 17:15:00 +02:00
|
|
|
}
|
2011-11-08 14:30:33 +01:00
|
|
|
}), name)
|
2011-04-21 21:31:24 +02:00
|
|
|
}
|