2010-12-26 22:49:40 +01:00
|
|
|
package akka.util
|
|
|
|
|
|
|
|
|
|
import akka.actor.{Actor, FSM}
|
|
|
|
|
import Actor._
|
|
|
|
|
import duration._
|
|
|
|
|
|
2010-12-30 22:43:24 +01:00
|
|
|
import java.util.concurrent.{BlockingDeque, LinkedBlockingDeque}
|
|
|
|
|
|
|
|
|
|
import scala.annotation.tailrec
|
2010-12-26 22:49:40 +01:00
|
|
|
|
2010-12-28 23:51:41 +01:00
|
|
|
object TestActor {
|
2010-12-29 21:47:05 +01:00
|
|
|
type Ignore = Option[PartialFunction[AnyRef, Boolean]]
|
|
|
|
|
|
2010-12-28 23:51:41 +01:00
|
|
|
case class SetTimeout(d : Duration)
|
2010-12-29 21:47:05 +01:00
|
|
|
case class SetIgnore(i : Ignore)
|
2010-12-28 23:51:41 +01:00
|
|
|
}
|
|
|
|
|
|
2010-12-30 22:43:24 +01:00
|
|
|
class TestActor(queue : BlockingDeque[AnyRef]) extends Actor with FSM[Int, TestActor.Ignore] {
|
2010-12-26 22:49:40 +01:00
|
|
|
import FSM._
|
2010-12-28 23:51:41 +01:00
|
|
|
import TestActor._
|
2010-12-26 22:49:40 +01:00
|
|
|
|
2010-12-29 21:47:05 +01:00
|
|
|
startWith(0, None)
|
2010-12-26 22:49:40 +01:00
|
|
|
when(0, stateTimeout = 5 seconds) {
|
2010-12-28 23:51:41 +01:00
|
|
|
case Event(SetTimeout(d), _) =>
|
|
|
|
|
setStateTimeout(0, if (d.finite_?) d else None)
|
|
|
|
|
stay
|
2010-12-29 21:47:05 +01:00
|
|
|
case Event(SetIgnore(ign), _) => stay using ign
|
2010-12-28 23:51:41 +01:00
|
|
|
case Event(StateTimeout, _) =>
|
|
|
|
|
stop
|
2010-12-29 21:47:05 +01:00
|
|
|
case Event(x : AnyRef, ign) =>
|
|
|
|
|
val ignore = ign map (z => if (z isDefinedAt x) z(x) else false) getOrElse false
|
|
|
|
|
if (!ignore) {
|
2010-12-30 22:43:24 +01:00
|
|
|
queue.offerLast(x)
|
2010-12-29 21:47:05 +01:00
|
|
|
}
|
2010-12-26 22:49:40 +01:00
|
|
|
stay
|
|
|
|
|
}
|
|
|
|
|
initialize
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-28 23:51:41 +01:00
|
|
|
/**
|
|
|
|
|
* Test kit for testing actors. Inheriting from this trait enables reception of
|
|
|
|
|
* replies from actors, which are queued by an internal actor and can be
|
2010-12-29 21:47:05 +01:00
|
|
|
* examined using the `expectMsg...` methods. Assertions and bounds concerning
|
2010-12-28 23:51:41 +01:00
|
|
|
* timing are available in the form of `within` blocks.
|
|
|
|
|
*
|
|
|
|
|
* <pre>
|
|
|
|
|
* class Test extends TestKit {
|
|
|
|
|
* val test = actorOf[SomeActor].start
|
|
|
|
|
*
|
|
|
|
|
* within (1 second) {
|
|
|
|
|
* test ! SomeWork
|
2010-12-29 21:47:05 +01:00
|
|
|
* expectMsg(Result1) // bounded to 1 second
|
|
|
|
|
* expectMsg(Result2) // bounded to the remainder of the 1 second
|
2010-12-28 23:51:41 +01:00
|
|
|
* }
|
|
|
|
|
* }
|
|
|
|
|
* </pre>
|
|
|
|
|
*
|
|
|
|
|
* Beware of two points:
|
|
|
|
|
*
|
|
|
|
|
* - the internal test actor needs to be stopped, either explicitly using
|
|
|
|
|
* `stopTestActor` or implicitly by using its internal inactivity timeout,
|
|
|
|
|
* see `setTestActorTimeout`
|
|
|
|
|
* - this trait is not thread-safe (only one actor with one queue, one stack
|
|
|
|
|
* of `within` blocks); it is expected that the code is executed from a
|
|
|
|
|
* constructor as shown above, which makes this a non-issue, otherwise take
|
2010-12-29 21:47:05 +01:00
|
|
|
* care not to run tests within a single test class instance in parallel.
|
|
|
|
|
*
|
|
|
|
|
* @author Roland Kuhn
|
|
|
|
|
* @since 1.1
|
2010-12-28 23:51:41 +01:00
|
|
|
*/
|
|
|
|
|
trait TestKit {
|
2010-12-26 22:49:40 +01:00
|
|
|
|
2010-12-30 22:43:24 +01:00
|
|
|
private val queue = new LinkedBlockingDeque[AnyRef]()
|
2010-12-26 22:49:40 +01:00
|
|
|
|
2010-12-29 21:47:05 +01:00
|
|
|
/**
|
|
|
|
|
* ActorRef of the test actor. Access is provided to enable e.g.
|
|
|
|
|
* registration as message target.
|
|
|
|
|
*/
|
|
|
|
|
protected val testActor = actorOf(new TestActor(queue)).start
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Implicit sender reference so that replies are possible for messages sent
|
|
|
|
|
* from the test class.
|
|
|
|
|
*/
|
|
|
|
|
protected implicit val senderOption = Some(testActor)
|
2010-12-26 22:49:40 +01:00
|
|
|
|
2010-12-28 23:51:41 +01:00
|
|
|
private var end : Duration = Duration.Inf
|
2010-12-30 22:43:24 +01:00
|
|
|
/*
|
|
|
|
|
* THIS IS A HACK: expectNoMsg and receiveWhile are bounded by `end`, but
|
|
|
|
|
* running them should not trigger an AssertionError, so mark their end
|
|
|
|
|
* time here and do not fail at the end of `within` if that time is not
|
|
|
|
|
* long gone.
|
|
|
|
|
*/
|
|
|
|
|
private var lastSoftTimeout : Duration = now - 5.millis
|
2010-12-28 23:51:41 +01:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Stop test actor. Should be done at the end of the test unless relying on
|
|
|
|
|
* test actor timeout.
|
|
|
|
|
*/
|
2010-12-29 21:47:05 +01:00
|
|
|
def stopTestActor { testActor.stop }
|
2010-12-28 23:51:41 +01:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Set test actor timeout. By default, the test actor shuts itself down
|
|
|
|
|
* after 5 seconds of inactivity. Set this to Duration.Inf to disable this
|
|
|
|
|
* behavior, but make sure that someone will then call `stopTestActor`,
|
|
|
|
|
* unless you want to leak actors, e.g. wrap test in
|
|
|
|
|
*
|
|
|
|
|
* <pre>
|
|
|
|
|
* try {
|
|
|
|
|
* ...
|
|
|
|
|
* } finally { stopTestActor }
|
|
|
|
|
* </pre>
|
|
|
|
|
*/
|
2010-12-29 21:47:05 +01:00
|
|
|
def setTestActorTimeout(d : Duration) { testActor ! TestActor.SetTimeout(d) }
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Ignore all messages in the test actor for which the given partial
|
|
|
|
|
* function returns true.
|
|
|
|
|
*/
|
|
|
|
|
def ignoreMsg(f : PartialFunction[AnyRef, Boolean]) { testActor ! TestActor.SetIgnore(Some(f)) }
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Stop ignoring messages in the test actor.
|
|
|
|
|
*/
|
|
|
|
|
def ignoreNoMsg { testActor ! TestActor.SetIgnore(None) }
|
2010-12-28 23:51:41 +01:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Obtain current time (`System.currentTimeMillis`) as Duration.
|
|
|
|
|
*/
|
2010-12-30 22:43:24 +01:00
|
|
|
def now : Duration = System.nanoTime.nanos
|
2010-12-26 22:49:40 +01:00
|
|
|
|
2010-12-28 23:51:41 +01:00
|
|
|
/**
|
|
|
|
|
* Obtain time remaining for execution of the innermost enclosing `within` block.
|
|
|
|
|
*/
|
|
|
|
|
def remaining : Duration = end - now
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Execute code block while bounding its execution time between `min` and
|
|
|
|
|
* `max`. `within` blocks may be nested. All methods in this trait which
|
|
|
|
|
* take maximum wait times are available in a version which implicitly uses
|
|
|
|
|
* the remaining time governed by the innermost enclosing `within` block.
|
|
|
|
|
*
|
|
|
|
|
* <pre>
|
|
|
|
|
* val ret = within(50 millis) {
|
|
|
|
|
* test ! "ping"
|
2010-12-29 21:47:05 +01:00
|
|
|
* expectMsgClass(classOf[String])
|
2010-12-28 23:51:41 +01:00
|
|
|
* }
|
|
|
|
|
* </pre>
|
|
|
|
|
*/
|
2010-12-26 22:49:40 +01:00
|
|
|
def within[T](min : Duration, max : Duration)(f : => T) : T = {
|
|
|
|
|
val start = now
|
2010-12-28 23:51:41 +01:00
|
|
|
val rem = end - start
|
|
|
|
|
assert (rem >= min, "required min time "+min+" not possible, only "+rem+" left")
|
|
|
|
|
|
|
|
|
|
val max_diff = if (max < rem) max else rem
|
|
|
|
|
val prev_end = end
|
|
|
|
|
end = start + max_diff
|
|
|
|
|
|
2010-12-26 22:49:40 +01:00
|
|
|
val ret = f
|
2010-12-28 23:51:41 +01:00
|
|
|
|
|
|
|
|
val diff = now - start
|
|
|
|
|
assert (min <= diff, "block took "+diff+", should at least have been "+min)
|
2010-12-30 22:43:24 +01:00
|
|
|
/*
|
|
|
|
|
* caution: HACK AHEAD
|
|
|
|
|
*/
|
|
|
|
|
if (now - lastSoftTimeout > 5.millis) {
|
|
|
|
|
assert (diff <= max_diff, "block took "+diff+", exceeding "+max_diff)
|
|
|
|
|
} else {
|
|
|
|
|
lastSoftTimeout -= 5.millis
|
|
|
|
|
}
|
2010-12-28 23:51:41 +01:00
|
|
|
|
|
|
|
|
end = prev_end
|
2010-12-26 22:49:40 +01:00
|
|
|
ret
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-28 23:51:41 +01:00
|
|
|
/**
|
|
|
|
|
* Same as calling `within(0 seconds, max)(f)`.
|
|
|
|
|
*/
|
|
|
|
|
def within[T](max : Duration)(f : => T) : T = within(0 seconds, max)(f)
|
|
|
|
|
|
|
|
|
|
/**
|
2010-12-29 21:47:05 +01:00
|
|
|
* Same as `expectMsg`, but takes the maximum wait time from the innermost
|
2010-12-28 23:51:41 +01:00
|
|
|
* enclosing `within` block.
|
|
|
|
|
*/
|
2010-12-29 21:47:05 +01:00
|
|
|
def expectMsg(obj : Any) : AnyRef = expectMsg(remaining, obj)
|
2010-12-28 23:51:41 +01:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Receive one message from the test actor and assert that it equals the
|
|
|
|
|
* given object. Wait time is bounded by the given duration, with an
|
|
|
|
|
* AssertionFailure being thrown in case of timeout.
|
|
|
|
|
*
|
|
|
|
|
* @return the received object
|
|
|
|
|
*/
|
2010-12-29 21:47:05 +01:00
|
|
|
def expectMsg(max : Duration, obj : Any) : AnyRef = {
|
2010-12-29 11:23:24 +01:00
|
|
|
val o = receiveOne(max)
|
2010-12-29 21:47:05 +01:00
|
|
|
assert (o ne null, "timeout during expectMsg")
|
2010-12-26 22:49:40 +01:00
|
|
|
assert (obj == o, "expected "+obj+", found "+o)
|
|
|
|
|
o
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-28 23:51:41 +01:00
|
|
|
/**
|
2010-12-29 21:47:05 +01:00
|
|
|
* Same as `expectMsg`, but takes the maximum wait time from the innermost
|
2010-12-28 23:51:41 +01:00
|
|
|
* enclosing `within` block.
|
|
|
|
|
*/
|
2010-12-29 21:47:05 +01:00
|
|
|
def expectMsg[T](f : PartialFunction[Any, T]) : T = expectMsg(remaining)(f)
|
2010-12-28 23:51:41 +01:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Receive one message from the test actor and assert that the given
|
|
|
|
|
* partial function accepts it. Wait time is bounded by the given duration,
|
|
|
|
|
* with an AssertionFailure being thrown in case of timeout.
|
|
|
|
|
*
|
|
|
|
|
* Use this variant to implement more complicated or conditional
|
|
|
|
|
* processing.
|
|
|
|
|
*
|
|
|
|
|
* @return the received object as transformed by the partial function
|
|
|
|
|
*/
|
2010-12-29 21:47:05 +01:00
|
|
|
def expectMsg[T](max : Duration)(f : PartialFunction[Any, T]) : T = {
|
2010-12-29 11:23:24 +01:00
|
|
|
val o = receiveOne(max)
|
2010-12-29 21:47:05 +01:00
|
|
|
assert (o ne null, "timeout during expectMsg")
|
2010-12-28 23:51:41 +01:00
|
|
|
assert (f.isDefinedAt(o), "does not match: "+o)
|
|
|
|
|
f(o)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2010-12-29 21:47:05 +01:00
|
|
|
* Same as `expectMsgClass`, but takes the maximum wait time from the innermost
|
2010-12-28 23:51:41 +01:00
|
|
|
* enclosing `within` block.
|
|
|
|
|
*/
|
2010-12-29 21:47:05 +01:00
|
|
|
def expectMsgClass[C](c : Class[C]) : C = expectMsgClass(remaining, c)
|
2010-12-28 23:51:41 +01:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Receive one message from the test actor and assert that it conforms to
|
|
|
|
|
* the given class. Wait time is bounded by the given duration, with an
|
|
|
|
|
* AssertionFailure being thrown in case of timeout.
|
|
|
|
|
*
|
|
|
|
|
* @return the received object
|
|
|
|
|
*/
|
2010-12-29 21:47:05 +01:00
|
|
|
def expectMsgClass[C](max : Duration, c : Class[C]) : C = {
|
2010-12-29 11:23:24 +01:00
|
|
|
val o = receiveOne(max)
|
2010-12-29 21:47:05 +01:00
|
|
|
assert (o ne null, "timeout during expectMsgClass")
|
2010-12-28 23:51:41 +01:00
|
|
|
assert (c isInstance o, "expected "+c+", found "+o.getClass)
|
|
|
|
|
o.asInstanceOf[C]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2010-12-29 21:47:05 +01:00
|
|
|
* Same as `expectMsgAnyOf`, but takes the maximum wait time from the innermost
|
2010-12-28 23:51:41 +01:00
|
|
|
* enclosing `within` block.
|
|
|
|
|
*/
|
2010-12-29 21:47:05 +01:00
|
|
|
def expectMsgAnyOf(obj : Any*) : AnyRef = expectMsgAnyOf(remaining, obj : _*)
|
2010-12-28 23:51:41 +01:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Receive one message from the test actor and assert that it equals one of
|
|
|
|
|
* the given objects. Wait time is bounded by the given duration, with an
|
|
|
|
|
* AssertionFailure being thrown in case of timeout.
|
|
|
|
|
*
|
|
|
|
|
* @return the received object
|
|
|
|
|
*/
|
2010-12-29 21:47:05 +01:00
|
|
|
def expectMsgAnyOf(max : Duration, obj : Any*) : AnyRef = {
|
2010-12-29 11:23:24 +01:00
|
|
|
val o = receiveOne(max)
|
2010-12-29 21:47:05 +01:00
|
|
|
assert (o ne null, "timeout during expectMsgAnyOf")
|
2010-12-26 22:49:40 +01:00
|
|
|
assert (obj exists (_ == o), "found unexpected "+o)
|
|
|
|
|
o
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-28 23:51:41 +01:00
|
|
|
/**
|
2010-12-29 21:47:05 +01:00
|
|
|
* Same as `expectMsgAnyClassOf`, but takes the maximum wait time from the innermost
|
2010-12-28 23:51:41 +01:00
|
|
|
* enclosing `within` block.
|
|
|
|
|
*/
|
2010-12-29 21:47:05 +01:00
|
|
|
def expectMsgAnyClassOf(obj : Class[_]*) : AnyRef = expectMsgAnyClassOf(remaining, obj : _*)
|
2010-12-28 23:51:41 +01:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Receive one message from the test actor and assert that it conforms to
|
|
|
|
|
* one of the given classes. Wait time is bounded by the given duration,
|
|
|
|
|
* with an AssertionFailure being thrown in case of timeout.
|
|
|
|
|
*
|
|
|
|
|
* @return the received object
|
|
|
|
|
*/
|
2010-12-29 21:47:05 +01:00
|
|
|
def expectMsgAnyClassOf(max : Duration, obj : Class[_]*) : AnyRef = {
|
2010-12-29 11:23:24 +01:00
|
|
|
val o = receiveOne(max)
|
2010-12-29 21:47:05 +01:00
|
|
|
assert (o ne null, "timeout during expectMsgAnyClassOf")
|
2010-12-28 23:51:41 +01:00
|
|
|
assert (obj exists (_ isInstance o), "found unexpected "+o)
|
|
|
|
|
o
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2010-12-29 21:47:05 +01:00
|
|
|
* Same as `expectMsgAllOf`, but takes the maximum wait time from the innermost
|
2010-12-28 23:51:41 +01:00
|
|
|
* enclosing `within` block.
|
|
|
|
|
*/
|
2010-12-29 21:47:05 +01:00
|
|
|
def expectMsgAllOf(obj : Any*) { expectMsgAllOf(remaining, obj : _*) }
|
2010-12-28 23:51:41 +01:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Receive a number of messages from the test actor matching the given
|
|
|
|
|
* number of objects and assert that for each given object one is received
|
|
|
|
|
* which equals it. This construct is useful when the order in which the
|
|
|
|
|
* objects are received is not fixed. Wait time is bounded by the given
|
|
|
|
|
* duration, with an AssertionFailure being thrown in case of timeout.
|
|
|
|
|
*
|
|
|
|
|
* <pre>
|
|
|
|
|
* within(1 second) {
|
|
|
|
|
* dispatcher ! SomeWork1()
|
|
|
|
|
* dispatcher ! SomeWork2()
|
2010-12-29 21:47:05 +01:00
|
|
|
* expectMsgAllOf(Result1(), Result2())
|
2010-12-28 23:51:41 +01:00
|
|
|
* }
|
|
|
|
|
* </pre>
|
|
|
|
|
*/
|
2010-12-29 21:47:05 +01:00
|
|
|
def expectMsgAllOf(max : Duration, obj : Any*) {
|
2010-12-29 11:23:24 +01:00
|
|
|
val recv = receiveN(obj.size, now + max)
|
2010-12-26 22:49:40 +01:00
|
|
|
assert (obj forall (x => recv exists (x == _)), "not found all")
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-28 23:51:41 +01:00
|
|
|
/**
|
2010-12-29 21:47:05 +01:00
|
|
|
* Same as `expectMsgAllClassOf`, but takes the maximum wait time from the innermost
|
2010-12-28 23:51:41 +01:00
|
|
|
* enclosing `within` block.
|
|
|
|
|
*/
|
2010-12-29 21:47:05 +01:00
|
|
|
def expectMsgAllClassOf(obj : Class[_]*) { expectMsgAllClassOf(remaining, obj : _*) }
|
2010-12-28 23:51:41 +01:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Receive a number of messages from the test actor matching the given
|
|
|
|
|
* number of classes and assert that for each given class one is received
|
|
|
|
|
* which is of that class (equality, not conformance). This construct is
|
|
|
|
|
* useful when the order in which the objects are received is not fixed.
|
|
|
|
|
* Wait time is bounded by the given duration, with an AssertionFailure
|
|
|
|
|
* being thrown in case of timeout.
|
|
|
|
|
*/
|
2010-12-29 21:47:05 +01:00
|
|
|
def expectMsgAllClassOf(max : Duration, obj : Class[_]*) {
|
2010-12-29 11:23:24 +01:00
|
|
|
val recv = receiveN(obj.size, now + max)
|
2010-12-28 23:51:41 +01:00
|
|
|
assert (obj forall (x => recv exists (_.getClass eq x)), "not found all")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2010-12-29 21:47:05 +01:00
|
|
|
* Same as `expectMsgAllConformingOf`, but takes the maximum wait time from the innermost
|
2010-12-28 23:51:41 +01:00
|
|
|
* enclosing `within` block.
|
|
|
|
|
*/
|
2010-12-29 21:47:05 +01:00
|
|
|
def expectMsgAllConformingOf(obj : Class[_]*) { expectMsgAllClassOf(remaining, obj : _*) }
|
2010-12-28 23:51:41 +01:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Receive a number of messages from the test actor matching the given
|
|
|
|
|
* number of classes and assert that for each given class one is received
|
|
|
|
|
* which conforms to that class. This construct is useful when the order in
|
|
|
|
|
* which the objects are received is not fixed. Wait time is bounded by
|
|
|
|
|
* the given duration, with an AssertionFailure being thrown in case of
|
|
|
|
|
* timeout.
|
|
|
|
|
*
|
|
|
|
|
* Beware that one object may satisfy all given class constraints, which
|
|
|
|
|
* may be counter-intuitive.
|
|
|
|
|
*/
|
2010-12-29 21:47:05 +01:00
|
|
|
def expectMsgAllConformingOf(max : Duration, obj : Class[_]*) {
|
2010-12-29 11:23:24 +01:00
|
|
|
val recv = receiveN(obj.size, now + max)
|
|
|
|
|
assert (obj forall (x => recv exists (x isInstance _)), "not found all")
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-30 22:43:24 +01:00
|
|
|
/**
|
|
|
|
|
* Same as `expectNoMsg`, but takes the maximum wait time from the innermost
|
|
|
|
|
* enclosing `within` block.
|
|
|
|
|
*/
|
|
|
|
|
def expectNoMsg { expectNoMsg(remaining) }
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Assert that no message is received for the specified time.
|
|
|
|
|
*/
|
|
|
|
|
def expectNoMsg(max : Duration) {
|
|
|
|
|
val o = receiveOne(max)
|
|
|
|
|
assert (o eq null, "received unexpected message "+o)
|
|
|
|
|
lastSoftTimeout = now
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Same as `receiveWhile`, but takes the maximum wait time from the innermost
|
|
|
|
|
* enclosing `within` block.
|
|
|
|
|
*/
|
|
|
|
|
def receiveWhile[T](f : PartialFunction[AnyRef, T]) : Seq[T] = receiveWhile(remaining)(f)
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Receive a series of messages as long as the given partial function
|
|
|
|
|
* accepts them or the idle timeout is met or the overall maximum duration
|
|
|
|
|
* is elapsed. Returns the sequence of messages.
|
|
|
|
|
*
|
|
|
|
|
* Beware that the maximum duration is not implicitly bounded by or taken
|
|
|
|
|
* from the innermost enclosing `within` block, as it is not an error to
|
|
|
|
|
* hit the `max` duration in this case.
|
|
|
|
|
*
|
|
|
|
|
* One possible use of this method is for testing whether messages of
|
|
|
|
|
* certain characteristics are generated at a certain rate:
|
|
|
|
|
*
|
|
|
|
|
* <pre>
|
|
|
|
|
* test ! ScheduleTicks(100 millis)
|
|
|
|
|
* val series = receiveWhile(750 millis) {
|
|
|
|
|
* case Tick(count) => count
|
|
|
|
|
* }
|
|
|
|
|
* assert(series == (1 to 7).toList)
|
|
|
|
|
* </pre>
|
|
|
|
|
*/
|
|
|
|
|
def receiveWhile[T](max : Duration)(f : PartialFunction[AnyRef, T]) : Seq[T] = {
|
|
|
|
|
val stop = now + max
|
|
|
|
|
|
|
|
|
|
@tailrec def doit(acc : List[T]) : List[T] = {
|
|
|
|
|
receiveOne(stop - now) match {
|
|
|
|
|
case null =>
|
|
|
|
|
acc.reverse
|
|
|
|
|
case o if (f isDefinedAt o) =>
|
|
|
|
|
doit(f(o) :: acc)
|
|
|
|
|
case o =>
|
|
|
|
|
queue.offerFirst(o)
|
|
|
|
|
acc.reverse
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val ret = doit(Nil)
|
|
|
|
|
lastSoftTimeout = now
|
|
|
|
|
ret
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private def receiveN(n : Int, stop : Duration) : Seq[AnyRef] = {
|
2010-12-29 11:23:24 +01:00
|
|
|
for { x <- 1 to n } yield {
|
2010-12-28 23:51:41 +01:00
|
|
|
val timeout = stop - now
|
2010-12-29 11:23:24 +01:00
|
|
|
val o = receiveOne(timeout)
|
|
|
|
|
assert (o ne null, "timeout while expecting "+n+" messages")
|
2010-12-28 23:51:41 +01:00
|
|
|
o
|
|
|
|
|
}
|
2010-12-29 11:23:24 +01:00
|
|
|
}
|
|
|
|
|
|
2010-12-30 22:43:24 +01:00
|
|
|
private def receiveOne(max : Duration) : AnyRef = {
|
|
|
|
|
if (max == 0.seconds) {
|
|
|
|
|
queue.pollFirst
|
|
|
|
|
} else if (max.finite_?) {
|
|
|
|
|
queue.pollFirst(max.length, max.unit)
|
2010-12-29 11:23:24 +01:00
|
|
|
} else {
|
2010-12-30 22:43:24 +01:00
|
|
|
queue.takeFirst
|
2010-12-29 11:23:24 +01:00
|
|
|
}
|
2010-12-28 23:51:41 +01:00
|
|
|
}
|
2010-12-26 22:49:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// vim: set ts=4 sw=4 et:
|