make TestKit methods return most specific type
document all testkit methods
This commit is contained in:
parent
1e4084e843
commit
1970b96ae5
2 changed files with 178 additions and 45 deletions
|
|
@ -212,6 +212,125 @@ classes, receiving nothing for some time, etc.
|
|||
The test actor shuts itself down by default after 5 seconds (configurable)
|
||||
of inactivity, relieving you of the duty of explicitly managing it.
|
||||
|
||||
Built-In Assertions
|
||||
-------------------
|
||||
|
||||
The abovementioned :meth:`expectMsg` is not the only method for formulating
|
||||
assertions concerning received messages. Here is the full list:
|
||||
|
||||
* :meth:`expectMsg[T](d: Duration, msg: T): T`
|
||||
|
||||
The given message object must be received within the specified time; the
|
||||
object will be returned.
|
||||
|
||||
* :meth:`expectMsgPF[T](d: Duration)(pf: PartialFunction[Any, T]): T`
|
||||
|
||||
Within the given time period, a message must be received and the given
|
||||
partial function must be defined for that message; the result from applying
|
||||
the partial function to the received message is returned. The duration may
|
||||
be left unspecified (empty parentheses are required in this case) to use
|
||||
the deadline from the innermost enclosing :ref:`within <TestKit.within>`
|
||||
block instead.
|
||||
|
||||
* :meth:`expectMsgClass[T](d: Duration, c: Class[T]): T`
|
||||
|
||||
An object which is an instance of the given :class:`Class` must be received
|
||||
within the allotted time frame; the object will be returned. Note that this
|
||||
does a conformance check; if you need the class to be equal, have a look at
|
||||
:meth:`expectMsgAllClassOf` with a single given class argument.
|
||||
|
||||
* :meth:`expectMsgAnyOf[T](d: Duration, obj: T*): T`
|
||||
|
||||
An object must be received within the given time, and it must be equal (
|
||||
compared with ``==``) to at least one of the passed reference objects; the
|
||||
received object will be returned.
|
||||
|
||||
* :meth:`expectMsgAnyClassOf[T](d: Duration, obj: Class[_ <: T]*): T`
|
||||
|
||||
An object must be received within the given time, and it must be an
|
||||
instance of at least one of the supplied :class:`Class` objects; the
|
||||
received object will be returned.
|
||||
|
||||
* :meth:`expectMsgAllOf[T](d: Duration, obj: T*): Seq[T]`
|
||||
|
||||
A number of objects matching the size of the supplied object array must be
|
||||
received within the given time, and for each of the given objects there
|
||||
must exist at least one among the received ones which equals (compared with
|
||||
``==``) it. The full sequence of received objects is returned.
|
||||
|
||||
* :meth:`expectMsgAllClassOf[T](d: Duration, c: Class[_ <: T]*): Seq[T]`
|
||||
|
||||
A number of objects matching the size of the supplied :class:`Class` array
|
||||
must be received within the given time, and for each of the given classes
|
||||
there must exist at least one among the received objects whose class equals
|
||||
(compared with ``==``) it (this is *not* a conformance check). The full
|
||||
sequence of received objects is returned.
|
||||
|
||||
* :meth:`expectMsgAllConformingOf[T](d: Duration, c: Class[_ <: T]*): Seq[T]`
|
||||
|
||||
A number of objects matching the size of the supplied :class:`Class` array
|
||||
must be received within the given time, and for each of the given classes
|
||||
there must exist at least one among the received objects which is an
|
||||
instance of this class. The full sequence of received objects is returned.
|
||||
|
||||
* :meth:`expectNoMsg(d: Duration)`
|
||||
|
||||
No message must be received within the given time. This also fails if a
|
||||
message has been received before calling this method which has not been
|
||||
removed from the queue using one of the other methods.
|
||||
|
||||
* :meth:`receiveN(n: Int, d: Duration): Seq[AnyRef]`
|
||||
|
||||
``n`` messages must be received within the given time; the received
|
||||
messages are returned.
|
||||
|
||||
In addition to message reception assertions there are also methods which help
|
||||
with message flows:
|
||||
|
||||
* :meth:`receiveOne(d: Duration): AnyRef`
|
||||
|
||||
Tries to receive one message for at most the given time interval and
|
||||
returns ``null`` in case of failure. If the given Duration is zero, the
|
||||
call is non-blocking (polling mode).
|
||||
|
||||
* :meth:`receiveWhile[T](max: Duration, idle: Duration)(pf: PartialFunction[Any, T]): Seq[T]`
|
||||
|
||||
Collect messages as long as
|
||||
|
||||
* they are matching the given partial function
|
||||
* the given time interval is not used up
|
||||
* the next message is received within the idle timeout
|
||||
|
||||
All collected messages are returned. The maximum duration defaults to the
|
||||
time remaining in the innermost enclosing :ref:`within <TestKit.within>`
|
||||
block and the idle duration defaults to infinity (thereby disabling the
|
||||
idle timeout feature).
|
||||
|
||||
* :meth:`awaitCond(p: => Boolean, max: Duration, interval: Duration)`
|
||||
|
||||
Poll the given condition every :obj:`interval` until it returns ``true`` or
|
||||
the :obj:`max` duration is used up. The interval defaults to 100 ms and the
|
||||
maximum defaults to the time remaining in the innermost enclosing
|
||||
:ref:`within <TestKit.within>` block.
|
||||
|
||||
* :meth:`ignoreMsg(pf: PartialFunction[AnyRef, Boolean])`
|
||||
|
||||
:meth:`ignoreNoMsg`
|
||||
|
||||
The internal :obj:`testActor` contains a partial function for ignoring
|
||||
messages: it will only enqueue messages which do not match the function or
|
||||
for which the function returns ``false``. This function can be set and
|
||||
reset using the methods given above; each invocation replaces the previous
|
||||
function, they are not composed.
|
||||
|
||||
This feature is useful e.g. when testing a logging system, where you want
|
||||
to ignore regular messages and are only interested in your specific ones.
|
||||
|
||||
.. _TestKit.within:
|
||||
|
||||
Timing Assertions
|
||||
-----------------
|
||||
|
||||
Another important part of functional testing concerns timing: certain events
|
||||
must not happen immediately (like a timer), others need to happen before a
|
||||
deadline. Therefore, all examination methods accept an upper time limit within
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ trait TestKit {
|
|||
def ignoreNoMsg { testActor ! TestActor.SetIgnore(None) }
|
||||
|
||||
/**
|
||||
* Obtain current time (`System.currentTimeMillis`) as Duration.
|
||||
* Obtain current time (`System.nanoTime`) as Duration.
|
||||
*/
|
||||
def now: Duration = System.nanoTime.nanos
|
||||
|
||||
|
|
@ -241,7 +241,7 @@ trait TestKit {
|
|||
/**
|
||||
* Same as `expectMsg(remaining, obj)`, but correctly treating the timeFactor.
|
||||
*/
|
||||
def expectMsg(obj: Any): AnyRef = expectMsg_internal(remaining, obj)
|
||||
def expectMsg[T](obj: T): T = expectMsg_internal(remaining, obj)
|
||||
|
||||
/**
|
||||
* Receive one message from the test actor and assert that it equals the
|
||||
|
|
@ -250,13 +250,13 @@ trait TestKit {
|
|||
*
|
||||
* @return the received object
|
||||
*/
|
||||
def expectMsg(max: Duration, obj: Any): AnyRef = expectMsg_internal(max.dilated, obj)
|
||||
def expectMsg[T](max: Duration, obj: T): T = expectMsg_internal(max.dilated, obj)
|
||||
|
||||
private def expectMsg_internal(max: Duration, obj: Any): AnyRef = {
|
||||
private def expectMsg_internal[T](max: Duration, obj: T): T = {
|
||||
val o = receiveOne(max)
|
||||
assert(o ne null, "timeout during expectMsg")
|
||||
assert(obj == o, "expected " + obj + ", found " + o)
|
||||
o
|
||||
o.asInstanceOf[T]
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -320,7 +320,7 @@ trait TestKit {
|
|||
/**
|
||||
* Same as `expectMsgAnyOf(remaining, obj...)`, but correctly treating the timeFactor.
|
||||
*/
|
||||
def expectMsgAnyOf(obj: Any*): AnyRef = expectMsgAnyOf_internal(remaining, obj: _*)
|
||||
def expectMsgAnyOf[T](obj: T*): T = expectMsgAnyOf_internal(remaining, obj: _*)
|
||||
|
||||
/**
|
||||
* Receive one message from the test actor and assert that it equals one of
|
||||
|
|
@ -329,19 +329,19 @@ trait TestKit {
|
|||
*
|
||||
* @return the received object
|
||||
*/
|
||||
def expectMsgAnyOf(max: Duration, obj: Any*): AnyRef = expectMsgAnyOf_internal(max.dilated, obj: _*)
|
||||
def expectMsgAnyOf[T](max: Duration, obj: T*): T = expectMsgAnyOf_internal(max.dilated, obj: _*)
|
||||
|
||||
private def expectMsgAnyOf_internal(max: Duration, obj: Any*): AnyRef = {
|
||||
private def expectMsgAnyOf_internal[T](max: Duration, obj: T*): T = {
|
||||
val o = receiveOne(max)
|
||||
assert(o ne null, "timeout during expectMsgAnyOf")
|
||||
assert(obj exists (_ == o), "found unexpected " + o)
|
||||
o
|
||||
o.asInstanceOf[T]
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `expectMsgAnyClassOf(remaining, obj...)`, but correctly treating the timeFactor.
|
||||
*/
|
||||
def expectMsgAnyClassOf(obj: Class[_]*): AnyRef = expectMsgAnyClassOf_internal(remaining, obj: _*)
|
||||
def expectMsgAnyClassOf[C](obj: Class[_ <: C]*): C = expectMsgAnyClassOf_internal(remaining, obj: _*)
|
||||
|
||||
/**
|
||||
* Receive one message from the test actor and assert that it conforms to
|
||||
|
|
@ -350,26 +350,26 @@ trait TestKit {
|
|||
*
|
||||
* @return the received object
|
||||
*/
|
||||
def expectMsgAnyClassOf(max: Duration, obj: Class[_]*): AnyRef = expectMsgAnyClassOf_internal(max.dilated, obj: _*)
|
||||
def expectMsgAnyClassOf[C](max: Duration, obj: Class[_ <: C]*): C = expectMsgAnyClassOf_internal(max.dilated, obj: _*)
|
||||
|
||||
private def expectMsgAnyClassOf_internal(max: Duration, obj: Class[_]*): AnyRef = {
|
||||
private def expectMsgAnyClassOf_internal[C](max: Duration, obj: Class[_ <: C]*): C = {
|
||||
val o = receiveOne(max)
|
||||
assert(o ne null, "timeout during expectMsgAnyClassOf")
|
||||
assert(obj exists (_ isInstance o), "found unexpected " + o)
|
||||
o
|
||||
o.asInstanceOf[C]
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `expectMsgAllOf(remaining, obj...)`, but correctly treating the timeFactor.
|
||||
*/
|
||||
def expectMsgAllOf(obj: Any*) { expectMsgAllOf_internal(remaining, obj: _*) }
|
||||
def expectMsgAllOf[T](obj: T*): Seq[T] = expectMsgAllOf_internal(remaining, obj: _*)
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* which equals it and vice versa. 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) {
|
||||
|
|
@ -379,17 +379,19 @@ trait TestKit {
|
|||
* }
|
||||
* </pre>
|
||||
*/
|
||||
def expectMsgAllOf(max: Duration, obj: Any*) { expectMsgAllOf_internal(max.dilated, obj: _*) }
|
||||
def expectMsgAllOf[T](max: Duration, obj: T*): Seq[T] = expectMsgAllOf_internal(max.dilated, obj: _*)
|
||||
|
||||
private def expectMsgAllOf_internal(max: Duration, obj: Any*) {
|
||||
val recv = receiveN(obj.size, now + max)
|
||||
assert(obj forall (x ⇒ recv exists (x == _)), "not found all")
|
||||
private def expectMsgAllOf_internal[T](max: Duration, obj: T*): Seq[T] = {
|
||||
val recv = receiveN_internal(obj.size, max)
|
||||
obj foreach (x ⇒ assert(recv exists (x == _), "not found " + x))
|
||||
recv foreach (x ⇒ assert(obj exists (x == _), "found unexpected " + x))
|
||||
recv.asInstanceOf[Seq[T]]
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `expectMsgAllClassOf(remaining, obj...)`, but correctly treating the timeFactor.
|
||||
*/
|
||||
def expectMsgAllClassOf(obj: Class[_]*) { expectMsgAllClassOf_internal(remaining, obj: _*) }
|
||||
def expectMsgAllClassOf[T](obj: Class[_ <: T]*): Seq[T] = expectMsgAllClassOf_internal(remaining, obj: _*)
|
||||
|
||||
/**
|
||||
* Receive a number of messages from the test actor matching the given
|
||||
|
|
@ -399,34 +401,38 @@ trait TestKit {
|
|||
* Wait time is bounded by the given duration, with an AssertionFailure
|
||||
* being thrown in case of timeout.
|
||||
*/
|
||||
def expectMsgAllClassOf(max: Duration, obj: Class[_]*) { expectMsgAllClassOf_internal(max.dilated, obj: _*) }
|
||||
def expectMsgAllClassOf[T](max: Duration, obj: Class[_ <: T]*): Seq[T] = expectMsgAllClassOf_internal(max.dilated, obj: _*)
|
||||
|
||||
private def expectMsgAllClassOf_internal(max: Duration, obj: Class[_]*) {
|
||||
val recv = receiveN(obj.size, now + max)
|
||||
assert(obj forall (x ⇒ recv exists (_.getClass eq x)), "not found all")
|
||||
private def expectMsgAllClassOf_internal[T](max: Duration, obj: Class[_ <: T]*): Seq[T] = {
|
||||
val recv = receiveN_internal(obj.size, max)
|
||||
obj foreach (x ⇒ assert(recv exists (_.getClass eq x), "not found " + x))
|
||||
recv foreach (x ⇒ assert(obj exists (_ eq x.getClass), "found non-matching object " + x))
|
||||
recv.asInstanceOf[Seq[T]]
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `expectMsgAllConformingOf(remaining, obj...)`, but correctly treating the timeFactor.
|
||||
*/
|
||||
def expectMsgAllConformingOf(obj: Class[_]*) { expectMsgAllClassOf_internal(remaining, obj: _*) }
|
||||
def expectMsgAllConformingOf[T](obj: Class[_ <: T]*): Seq[T] = expectMsgAllClassOf_internal(remaining, obj: _*)
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* which conforms to that class (and vice versa). 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.
|
||||
*/
|
||||
def expectMsgAllConformingOf(max: Duration, obj: Class[_]*) { expectMsgAllConformingOf(max.dilated, obj: _*) }
|
||||
def expectMsgAllConformingOf[T](max: Duration, obj: Class[_ <: T]*): Seq[T] = expectMsgAllConformingOf(max.dilated, obj: _*)
|
||||
|
||||
private def expectMsgAllConformingOf_internal(max: Duration, obj: Class[_]*) {
|
||||
val recv = receiveN(obj.size, now + max)
|
||||
assert(obj forall (x ⇒ recv exists (x isInstance _)), "not found all")
|
||||
private def expectMsgAllConformingOf_internal[T](max: Duration, obj: Class[_ <: T]*): Seq[T] = {
|
||||
val recv = receiveN_internal(obj.size, max)
|
||||
obj foreach (x ⇒ assert(recv exists (x isInstance _), "not found " + x))
|
||||
recv foreach (x ⇒ assert(obj exists (_ isInstance x), "found non-matching object " + x))
|
||||
recv.asInstanceOf[Seq[T]]
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -448,12 +454,13 @@ trait TestKit {
|
|||
/**
|
||||
* Same as `receiveWhile(remaining)(f)`, but correctly treating the timeFactor.
|
||||
*/
|
||||
def receiveWhile[T](f: PartialFunction[AnyRef, T]): Seq[T] = receiveWhile_internal(remaining)(f)
|
||||
@deprecated("insert empty first parameter list", "1.2")
|
||||
def receiveWhile[T](f: PartialFunction[AnyRef, T]): Seq[T] = receiveWhile(remaining / Duration.timeFactor)(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.
|
||||
* Receive a series of messages until one does not match the given partial
|
||||
* function or the idle timeout is met (disabled by default) or the overall
|
||||
* maximum duration is elapsed. Returns the sequence of messages.
|
||||
*
|
||||
* Note that it is not an error to hit the `max` duration in this case.
|
||||
*
|
||||
|
|
@ -468,15 +475,13 @@ trait TestKit {
|
|||
* assert(series == (1 to 7).toList)
|
||||
* </pre>
|
||||
*/
|
||||
def receiveWhile[T](max: Duration)(f: PartialFunction[AnyRef, T]): Seq[T] = receiveWhile_internal(max.dilated)(f)
|
||||
|
||||
private def receiveWhile_internal[T](max: Duration)(f: PartialFunction[AnyRef, T]): Seq[T] = {
|
||||
val stop = now + max
|
||||
def receiveWhile[T](max: Duration = Duration.MinusInf, idle: Duration = Duration.Inf)(f: PartialFunction[AnyRef, T]): Seq[T] = {
|
||||
val stop = now + (if (max == Duration.MinusInf) remaining else max.dilated)
|
||||
var msg: Message = NullMessage
|
||||
|
||||
@tailrec
|
||||
def doit(acc: List[T]): List[T] = {
|
||||
receiveOne(stop - now)
|
||||
receiveOne((stop - now) min idle)
|
||||
lastMessage match {
|
||||
case NullMessage ⇒
|
||||
lastMessage = msg
|
||||
|
|
@ -496,10 +501,19 @@ trait TestKit {
|
|||
ret
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `receiveN(n, remaining)` but correctly taking into account
|
||||
* Duration.timeFactor.
|
||||
*/
|
||||
def receiveN(n: Int): Seq[AnyRef] = receiveN_internal(n, remaining)
|
||||
|
||||
/**
|
||||
* Receive N messages in a row before the given deadline.
|
||||
*/
|
||||
def receiveN(n: Int, stop: Duration): Seq[AnyRef] = {
|
||||
def receiveN(n: Int, max: Duration): Seq[AnyRef] = receiveN_internal(n, max.dilated)
|
||||
|
||||
private def receiveN_internal(n: Int, max: Duration): Seq[AnyRef] = {
|
||||
val stop = max + now
|
||||
for { x ← 1 to n } yield {
|
||||
val timeout = stop - now
|
||||
val o = receiveOne(timeout)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue