diff --git a/akka-actor-tests/src/test/scala/akka/actor/actor/ActorFireForgetRequestReplySpec.scala b/akka-actor-tests/src/test/scala/akka/actor/actor/ActorFireForgetRequestReplySpec.scala index 3f2f8e57db..c4e8ade76e 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/actor/ActorFireForgetRequestReplySpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/actor/ActorFireForgetRequestReplySpec.scala @@ -8,8 +8,8 @@ import org.scalatest.WordSpec import org.scalatest.matchers.MustMatchers import org.scalatest.BeforeAndAfterEach -import akka.testing._ -import akka.testing.Testing.sleepFor +import akka.testkit._ +import akka.testkit.Testing.sleepFor import akka.util.duration._ import Actor._ diff --git a/akka-actor-tests/src/test/scala/akka/actor/actor/ActorRefSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/actor/ActorRefSpec.scala index dcd58763d0..4bef71a74d 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/actor/ActorRefSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/actor/ActorRefSpec.scala @@ -7,9 +7,9 @@ package akka.actor import org.scalatest.WordSpec import org.scalatest.matchers.MustMatchers -import akka.testing._ +import akka.testkit._ import akka.util.duration._ -import akka.testing.Testing.sleepFor +import akka.testkit.Testing.sleepFor import akka.config.Supervision.{ OneForOneStrategy } import akka.dispatch.Future import java.util.concurrent.{ TimeUnit, CountDownLatch } diff --git a/akka-actor-tests/src/test/scala/akka/actor/actor/FSMActorSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/actor/FSMActorSpec.scala index 8e97f05500..0d06561d48 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/actor/FSMActorSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/actor/FSMActorSpec.scala @@ -7,7 +7,7 @@ package akka.actor import org.scalatest.WordSpec import org.scalatest.matchers.MustMatchers -import akka.testing._ +import akka.testkit._ import FSM._ import akka.util.Duration diff --git a/akka-actor-tests/src/test/scala/akka/actor/actor/FSMTransitionSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/actor/FSMTransitionSpec.scala index c9307ce02f..0cbaddd8e6 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/actor/FSMTransitionSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/actor/FSMTransitionSpec.scala @@ -6,7 +6,7 @@ package akka.actor import org.scalatest.WordSpec import org.scalatest.matchers.MustMatchers -import akka.testing._ +import akka.testkit._ import akka.testkit._ import akka.util.duration._ import akka.config.Supervision._ diff --git a/akka-actor-tests/src/test/scala/akka/actor/actor/ForwardActorSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/actor/ForwardActorSpec.scala index eea3355b74..78a1fbfd0a 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/actor/ForwardActorSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/actor/ForwardActorSpec.scala @@ -7,7 +7,7 @@ package akka.actor import org.scalatest.WordSpec import org.scalatest.matchers.MustMatchers -import akka.testing._ +import akka.testkit._ import akka.util.duration._ import Actor._ diff --git a/akka-actor-tests/src/test/scala/akka/actor/actor/HotSwapSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/actor/HotSwapSpec.scala index 2e5babcc34..53ce1fbbfd 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/actor/HotSwapSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/actor/HotSwapSpec.scala @@ -7,7 +7,7 @@ package akka.actor import org.scalatest.WordSpec import org.scalatest.matchers.MustMatchers -import akka.testing._ +import akka.testkit._ import Actor._ diff --git a/akka-actor-tests/src/test/scala/akka/actor/actor/ReceiveTimeoutSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/actor/ReceiveTimeoutSpec.scala index 491d37a6a3..a03193d4a6 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/actor/ReceiveTimeoutSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/actor/ReceiveTimeoutSpec.scala @@ -7,7 +7,7 @@ package akka.actor import org.scalatest.WordSpec import org.scalatest.matchers.MustMatchers -import akka.testing._ +import akka.testkit._ import akka.util.duration._ import Actor._ diff --git a/akka-actor-tests/src/test/scala/akka/actor/supervisor/SupervisorSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/supervisor/SupervisorSpec.scala index 8c5f397387..d3f3d65c27 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/supervisor/SupervisorSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/supervisor/SupervisorSpec.scala @@ -8,8 +8,8 @@ import org.scalatest.WordSpec import org.scalatest.matchers.MustMatchers import org.scalatest.BeforeAndAfterEach -import akka.testing._ -import akka.testing.Testing.{ testMillis, sleepFor } +import akka.testkit._ +import akka.testkit.Testing.sleepFor import akka.util.duration._ import akka.config.Supervision._ import akka.{ Die, Ping } @@ -20,7 +20,7 @@ import java.util.concurrent.LinkedBlockingQueue object SupervisorSpec { val Timeout = 5 seconds - val TimeoutMillis = testMillis(Timeout).toInt + val TimeoutMillis = Timeout.dilated.toMillis.toInt // ===================================================== // Message logs @@ -57,7 +57,7 @@ object SupervisorSpec { } class Master extends Actor { - self.faultHandler = OneForOneStrategy(List(classOf[Exception]), 5, testMillis(1 second).toInt) + self.faultHandler = OneForOneStrategy(List(classOf[Exception]), 5, (1 second).dilated.toMillis.toInt) val temp = { val a = actorOf[TemporaryActor] diff --git a/akka-actor-tests/src/test/scala/akka/actor/supervisor/SupervisorTreeSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/supervisor/SupervisorTreeSpec.scala index f55f8940cf..d8aaa9d0e4 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/supervisor/SupervisorTreeSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/supervisor/SupervisorTreeSpec.scala @@ -7,7 +7,7 @@ import org.scalatest.WordSpec import org.scalatest.matchers.MustMatchers import akka.util.duration._ -import akka.testing.Testing.sleepFor +import akka.testkit.Testing.sleepFor import akka.dispatch.Dispatchers import akka.config.Supervision.{ SupervisorConfig, OneForOneStrategy, Supervise, Permanent } import Actor._ diff --git a/akka-actor-tests/src/test/scala/akka/dispatch/ActorModelSpec.scala b/akka-actor-tests/src/test/scala/akka/dispatch/ActorModelSpec.scala index a6e24302f6..61d0da3555 100644 --- a/akka-actor-tests/src/test/scala/akka/dispatch/ActorModelSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/dispatch/ActorModelSpec.scala @@ -6,7 +6,7 @@ package akka.actor.dispatch import org.scalatest.junit.JUnitSuite import org.junit.Test import org.scalatest.Assertions._ -import akka.testing._ +import akka.testkit.Testing import akka.dispatch._ import akka.actor.Actor._ import java.util.concurrent.atomic.AtomicLong diff --git a/akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala b/akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala index 9c1704b7f5..35f72810ed 100644 --- a/akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala @@ -3,8 +3,8 @@ package akka.actor.routing import org.scalatest.WordSpec import org.scalatest.matchers.MustMatchers -import akka.testing._ -import akka.testing.Testing.{ sleepFor, testMillis } +import akka.testkit._ +import akka.testkit.Testing.sleepFor import akka.util.duration._ import akka.actor._ @@ -55,7 +55,7 @@ class RoutingSpec extends WordSpec with MustMatchers { case Test3 ⇒ t2 }.start() - implicit val timeout = Actor.Timeout(testMillis(5 seconds)) + implicit val timeout = Actor.Timeout((5 seconds).dilated) val result = for { a ← (d ? (Test1)).as[Int] b ← (d ? (Test2)).as[Int] diff --git a/akka-actor/src/main/scala/akka/util/Duration.scala b/akka-actor/src/main/scala/akka/util/Duration.scala index 2e4b69756b..6e00ef0c17 100644 --- a/akka-actor/src/main/scala/akka/util/Duration.scala +++ b/akka-actor/src/main/scala/akka/util/Duration.scala @@ -90,6 +90,15 @@ object Duration { case "ns" | "nano" | "nanos" | "nanosecond" | "nanoseconds" ⇒ NANOSECONDS } + /* + * Testing facilities + */ + val timeFactor: Double = { + val factor = System.getProperty("akka.test.timefactor", "1.0") + try { factor.toDouble } + catch { case e: java.lang.NumberFormatException ⇒ 1.0 } + } + val Zero: Duration = new FiniteDuration(0, NANOSECONDS) trait Infinite { @@ -234,6 +243,9 @@ abstract class Duration { def /(other: Duration): Double def unary_- : Duration def finite_? : Boolean + def dilated: Duration = this * Duration.timeFactor + def min(other: Duration): Duration = if (this < other) this else other + def max(other: Duration): Duration = if (this > other) this else other // Java API def lt(other: Duration) = this < other diff --git a/akka-actor-tests/src/main/scala/akka/testing/TestBarrier.scala b/akka-testkit/src/main/scala/akka/testkit/TestBarrier.scala similarity index 92% rename from akka-actor-tests/src/main/scala/akka/testing/TestBarrier.scala rename to akka-testkit/src/main/scala/akka/testkit/TestBarrier.scala index 8806bf903e..87c258b255 100644 --- a/akka-actor-tests/src/main/scala/akka/testing/TestBarrier.scala +++ b/akka-testkit/src/main/scala/akka/testkit/TestBarrier.scala @@ -2,7 +2,7 @@ * Copyright (C) 2009-2011 Scalable Solutions AB */ -package akka.testing +package akka.testkit import akka.util.Duration import java.util.concurrent.{ CyclicBarrier, TimeUnit, TimeoutException } @@ -31,7 +31,7 @@ class TestBarrier(count: Int) { barrier.await(Testing.testTime(timeout.toNanos), TimeUnit.NANOSECONDS) } catch { case e: TimeoutException ⇒ - throw new TestBarrierTimeoutException("Timeout of %s and time factor of %s" format (timeout.toString, Testing.timeFactor)) + throw new TestBarrierTimeoutException("Timeout of %s and time factor of %s" format (timeout.toString, Duration.timeFactor)) } } diff --git a/akka-testkit/src/main/scala/akka/testkit/TestKit.scala b/akka-testkit/src/main/scala/akka/testkit/TestKit.scala index 4598749ae2..f9d31fd859 100644 --- a/akka-testkit/src/main/scala/akka/testkit/TestKit.scala +++ b/akka-testkit/src/main/scala/akka/testkit/TestKit.scala @@ -81,6 +81,10 @@ class TestActor(queue: BlockingDeque[TestActor.Message]) extends Actor with FSM[ * constructor as shown above, which makes this a non-issue, otherwise take * care not to run tests within a single test class instance in parallel. * + * It should be noted that for CI servers and the like all maximum Durations + * are scaled using their Duration.dilated method, which uses the + * Duration.timeFactor settable via akka.conf entry "akka.test.timefactor". + * * @author Roland Kuhn * @since 1.1 */ @@ -154,12 +158,40 @@ trait TestKit { */ def remaining: Duration = end - now + /** + * Block until the given condition evaluates to `true` or the timeout + * expires, whichever comes first. + * + * If no timeout is given, take it from the innermost enclosing `within` + * block. + * + * Note that the timeout is scaled using Duration.timeFactor. + */ + def awaitCond(p: ⇒ Boolean, max: Duration = Duration.MinusInf, interval: Duration = 100.millis) { + val _max = if (max eq Duration.MinusInf) remaining else max.dilated + val stop = now + _max + + @tailrec + def poll(t: Duration) { + if (!p) { + assert(now < stop, "timeout " + _max + " expired") + Thread.sleep(t.toMillis) + poll((stop - now) min interval) + } + } + + poll(_max min interval) + } + /** * 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. * + * Note that the max Duration is scaled by Duration.timeFactor while the min + * Duration is not. + * *
    * val ret = within(50 millis) {
    *         test ! "ping"
@@ -168,11 +200,12 @@ trait TestKit {
    * 
*/ def within[T](min: Duration, max: Duration)(f: ⇒ T): T = { + val _max = max.dilated val start = now val rem = end - start assert(rem >= min, "required min time " + min + " not possible, only " + format(min.unit, rem) + " left") - val max_diff = if (max < rem) max else rem + val max_diff = _max min rem val prev_end = end end = start + max_diff @@ -184,7 +217,7 @@ trait TestKit { * caution: HACK AHEAD */ if (now - lastSoftTimeout > 5.millis) { - assert(diff <= max_diff, "block took " + format(max.unit, diff) + ", exceeding " + format(max.unit, max_diff)) + assert(diff <= max_diff, "block took " + format(_max.unit, diff) + ", exceeding " + format(_max.unit, max_diff)) } else { lastSoftTimeout -= 5.millis } @@ -206,10 +239,9 @@ trait TestKit { def reply(msg: AnyRef) { lastMessage.channel ! msg } /** - * Same as `expectMsg`, but takes the maximum wait time from the innermost - * enclosing `within` block. + * Same as `expectMsg(remaining, obj)`, but correctly treating the timeFactor. */ - def expectMsg(obj: Any): AnyRef = expectMsg(remaining, obj) + def expectMsg(obj: Any): AnyRef = expectMsg_internal(remaining, obj) /** * Receive one message from the test actor and assert that it equals the @@ -218,7 +250,9 @@ trait TestKit { * * @return the received object */ - def expectMsg(max: Duration, obj: Any): AnyRef = { + def expectMsg(max: Duration, obj: Any): AnyRef = expectMsg_internal(max.dilated, obj) + + private def expectMsg_internal(max: Duration, obj: Any): AnyRef = { val o = receiveOne(max) assert(o ne null, "timeout during expectMsg") assert(obj == o, "expected " + obj + ", found " + o) @@ -226,10 +260,15 @@ trait TestKit { } /** - * Same as `expectMsg`, but takes the maximum wait time from the innermost - * enclosing `within` block. + * Same as `expectMsg(remaining)(f)`, but correctly treating the timeFactor. */ - def expectMsg[T](f: PartialFunction[Any, T]): T = expectMsg(remaining)(f) + @deprecated("use expectMsgPF instead", "1.2") + def expectMsg[T](f: PartialFunction[Any, T]): T = expectMsgPF_internal(remaining)(f) + + /** + * Same as `expectMsgPF(remaining)(f)`, but correctly treating the timeFactor. + */ + def expectMsgPF[T](f: PartialFunction[Any, T]): T = expectMsgPF_internal(remaining)(f) /** * Receive one message from the test actor and assert that the given @@ -241,7 +280,22 @@ trait TestKit { * * @return the received object as transformed by the partial function */ - def expectMsg[T](max: Duration)(f: PartialFunction[Any, T]): T = { + @deprecated("use expectMsgPF instead", "1.2") + def expectMsg[T](max: Duration)(f: PartialFunction[Any, T]): T = expectMsgPF_internal(max.dilated)(f) + + /** + * 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 + */ + def expectMsgPF[T](max: Duration)(f: PartialFunction[Any, T]): T = expectMsgPF_internal(max.dilated)(f) + + private def expectMsgPF_internal[T](max: Duration)(f: PartialFunction[Any, T]): T = { val o = receiveOne(max) assert(o ne null, "timeout during expectMsg") assert(f.isDefinedAt(o), "does not match: " + o) @@ -249,10 +303,9 @@ trait TestKit { } /** - * Same as `expectMsgClass`, but takes the maximum wait time from the innermost - * enclosing `within` block. + * Same as `expectMsgClass(remaining, c)`, but correctly treating the timeFactor. */ - def expectMsgClass[C](c: Class[C]): C = expectMsgClass(remaining, c) + def expectMsgClass[C](c: Class[C]): C = expectMsgClass_internal(remaining, c) /** * Receive one message from the test actor and assert that it conforms to @@ -261,7 +314,9 @@ trait TestKit { * * @return the received object */ - def expectMsgClass[C](max: Duration, c: Class[C]): C = { + def expectMsgClass[C](max: Duration, c: Class[C]): C = expectMsgClass_internal(max.dilated, c) + + private def expectMsgClass_internal[C](max: Duration, c: Class[C]): C = { val o = receiveOne(max) assert(o ne null, "timeout during expectMsgClass") assert(c isInstance o, "expected " + c + ", found " + o.getClass) @@ -269,10 +324,9 @@ trait TestKit { } /** - * Same as `expectMsgAnyOf`, but takes the maximum wait time from the innermost - * enclosing `within` block. + * Same as `expectMsgAnyOf(remaining, obj...)`, but correctly treating the timeFactor. */ - def expectMsgAnyOf(obj: Any*): AnyRef = expectMsgAnyOf(remaining, obj: _*) + def expectMsgAnyOf(obj: Any*): AnyRef = expectMsgAnyOf_internal(remaining, obj: _*) /** * Receive one message from the test actor and assert that it equals one of @@ -281,7 +335,9 @@ trait TestKit { * * @return the received object */ - def expectMsgAnyOf(max: Duration, obj: Any*): AnyRef = { + def expectMsgAnyOf(max: Duration, obj: Any*): AnyRef = expectMsgAnyOf_internal(max.dilated, obj: _*) + + private def expectMsgAnyOf_internal(max: Duration, obj: Any*): AnyRef = { val o = receiveOne(max) assert(o ne null, "timeout during expectMsgAnyOf") assert(obj exists (_ == o), "found unexpected " + o) @@ -289,10 +345,9 @@ trait TestKit { } /** - * Same as `expectMsgAnyClassOf`, but takes the maximum wait time from the innermost - * enclosing `within` block. + * Same as `expectMsgAnyClassOf(remaining, obj...)`, but correctly treating the timeFactor. */ - def expectMsgAnyClassOf(obj: Class[_]*): AnyRef = expectMsgAnyClassOf(remaining, obj: _*) + def expectMsgAnyClassOf(obj: Class[_]*): AnyRef = expectMsgAnyClassOf_internal(remaining, obj: _*) /** * Receive one message from the test actor and assert that it conforms to @@ -301,7 +356,9 @@ trait TestKit { * * @return the received object */ - def expectMsgAnyClassOf(max: Duration, obj: Class[_]*): AnyRef = { + def expectMsgAnyClassOf(max: Duration, obj: Class[_]*): AnyRef = expectMsgAnyClassOf_internal(max.dilated, obj: _*) + + private def expectMsgAnyClassOf_internal(max: Duration, obj: Class[_]*): AnyRef = { val o = receiveOne(max) assert(o ne null, "timeout during expectMsgAnyClassOf") assert(obj exists (_ isInstance o), "found unexpected " + o) @@ -309,10 +366,9 @@ trait TestKit { } /** - * Same as `expectMsgAllOf`, but takes the maximum wait time from the innermost - * enclosing `within` block. + * Same as `expectMsgAllOf(remaining, obj...)`, but correctly treating the timeFactor. */ - def expectMsgAllOf(obj: Any*) { expectMsgAllOf(remaining, obj: _*) } + def expectMsgAllOf(obj: Any*) { expectMsgAllOf_internal(remaining, obj: _*) } /** * Receive a number of messages from the test actor matching the given @@ -329,16 +385,17 @@ trait TestKit { * } * */ - def expectMsgAllOf(max: Duration, obj: Any*) { + def expectMsgAllOf(max: Duration, obj: Any*) { 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") } /** - * Same as `expectMsgAllClassOf`, but takes the maximum wait time from the innermost - * enclosing `within` block. + * Same as `expectMsgAllClassOf(remaining, obj...)`, but correctly treating the timeFactor. */ - def expectMsgAllClassOf(obj: Class[_]*) { expectMsgAllClassOf(remaining, obj: _*) } + def expectMsgAllClassOf(obj: Class[_]*) { expectMsgAllClassOf_internal(remaining, obj: _*) } /** * Receive a number of messages from the test actor matching the given @@ -348,16 +405,17 @@ 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[_]*) { + def expectMsgAllClassOf(max: Duration, obj: Class[_]*) { 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") } /** - * Same as `expectMsgAllConformingOf`, but takes the maximum wait time from the innermost - * enclosing `within` block. + * Same as `expectMsgAllConformingOf(remaining, obj...)`, but correctly treating the timeFactor. */ - def expectMsgAllConformingOf(obj: Class[_]*) { expectMsgAllClassOf(remaining, obj: _*) } + def expectMsgAllConformingOf(obj: Class[_]*) { expectMsgAllClassOf_internal(remaining, obj: _*) } /** * Receive a number of messages from the test actor matching the given @@ -370,40 +428,40 @@ trait TestKit { * Beware that one object may satisfy all given class constraints, which * may be counter-intuitive. */ - def expectMsgAllConformingOf(max: Duration, obj: Class[_]*) { + def expectMsgAllConformingOf(max: Duration, obj: Class[_]*) { 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") } /** - * Same as `expectNoMsg`, but takes the maximum wait time from the innermost - * enclosing `within` block. + * Same as `expectNoMsg(remaining)`, but correctly treating the timeFactor. */ - def expectNoMsg { expectNoMsg(remaining) } + def expectNoMsg { expectNoMsg_internal(remaining) } /** * Assert that no message is received for the specified time. */ - def expectNoMsg(max: Duration) { + def expectNoMsg(max: Duration) { expectNoMsg_internal(max.dilated) } + + private def expectNoMsg_internal(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. + * Same as `receiveWhile(remaining)(f)`, but correctly treating the timeFactor. */ - def receiveWhile[T](f: PartialFunction[AnyRef, T]): Seq[T] = receiveWhile(remaining)(f) + def receiveWhile[T](f: PartialFunction[AnyRef, T]): Seq[T] = receiveWhile_internal(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. + * Note that 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: @@ -416,7 +474,9 @@ trait TestKit { * assert(series == (1 to 7).toList) * */ - def receiveWhile[T](max: Duration)(f: PartialFunction[AnyRef, T]): Seq[T] = { + 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 var msg: Message = NullMessage @@ -457,6 +517,8 @@ trait TestKit { /** * Receive one message from the internal queue of the TestActor. If the given * duration is zero, the queue is polled (non-blocking). + * + * This method does NOT automatically scale its Duration parameter! */ def receiveOne(max: Duration): AnyRef = { val message = diff --git a/akka-actor-tests/src/main/scala/akka/testing/TestLatch.scala b/akka-testkit/src/main/scala/akka/testkit/TestLatch.scala similarity index 94% rename from akka-actor-tests/src/main/scala/akka/testing/TestLatch.scala rename to akka-testkit/src/main/scala/akka/testkit/TestLatch.scala index 239aa6987f..7240149c45 100644 --- a/akka-actor-tests/src/main/scala/akka/testing/TestLatch.scala +++ b/akka-testkit/src/main/scala/akka/testkit/TestLatch.scala @@ -2,7 +2,7 @@ * Copyright (C) 2009-2011 Scalable Solutions AB */ -package akka.testing +package akka.testkit import akka.util.Duration import java.util.concurrent.{ CountDownLatch, TimeUnit } @@ -35,7 +35,7 @@ class TestLatch(count: Int = 1) { def await(timeout: Duration): Boolean = { val opened = latch.await(Testing.testTime(timeout.toNanos), TimeUnit.NANOSECONDS) if (!opened) throw new TestLatchTimeoutException( - "Timeout of %s with time factor of %s" format (timeout.toString, Testing.timeFactor)) + "Timeout of %s with time factor of %s" format (timeout.toString, Duration.timeFactor)) opened } @@ -45,7 +45,7 @@ class TestLatch(count: Int = 1) { def awaitTimeout(timeout: Duration = TestLatch.DefaultTimeout) = { val opened = latch.await(Testing.testTime(timeout.toNanos), TimeUnit.NANOSECONDS) if (opened) throw new TestLatchNoTimeoutException( - "Latch opened before timeout of %s with time factor of %s" format (timeout.toString, Testing.timeFactor)) + "Latch opened before timeout of %s with time factor of %s" format (timeout.toString, Duration.timeFactor)) opened } diff --git a/akka-actor-tests/src/main/scala/akka/testing/Testing.scala b/akka-testkit/src/main/scala/akka/testkit/Testing.scala similarity index 57% rename from akka-actor-tests/src/main/scala/akka/testing/Testing.scala rename to akka-testkit/src/main/scala/akka/testkit/Testing.scala index 730f35f840..889b87920b 100644 --- a/akka-actor-tests/src/main/scala/akka/testing/Testing.scala +++ b/akka-testkit/src/main/scala/akka/testkit/Testing.scala @@ -2,32 +2,20 @@ * Copyright (C) 2009-2011 Scalable Solutions AB */ -package akka.testing +package akka.testkit import akka.util.Duration +import Duration.timeFactor /** * Multiplying numbers used in test timeouts by a factor, set by system property. * Useful for Jenkins builds (where the machine may need more time). */ object Testing { - val timeFactor: Double = { - val factor = System.getProperty("akka.test.timefactor", "1.0") - try { - factor.toDouble - } catch { - case e: java.lang.NumberFormatException ⇒ 1.0 - } - } - def testTime(t: Int): Int = (timeFactor * t).toInt def testTime(t: Long): Long = (timeFactor * t).toLong def testTime(t: Float): Float = (timeFactor * t).toFloat def testTime(t: Double): Double = timeFactor * t - def testSeconds(duration: Duration) = testTime(duration.toSeconds) - def testMillis(duration: Duration) = testTime(duration.toMillis) - def testNanos(duration: Duration) = testTime(duration.toNanos) - def sleepFor(duration: Duration) = Thread.sleep(testTime(duration.toMillis)) } diff --git a/akka-testkit/src/test/scala/akka/testkit/TestTimeSpec.scala b/akka-testkit/src/test/scala/akka/testkit/TestTimeSpec.scala new file mode 100644 index 0000000000..55d19288c9 --- /dev/null +++ b/akka-testkit/src/test/scala/akka/testkit/TestTimeSpec.scala @@ -0,0 +1,36 @@ +package akka.testkit + +import org.scalatest.matchers.MustMatchers +import org.scalatest.{ BeforeAndAfterEach, WordSpec } +import akka.util.Duration + +class TestTimeSpec extends WordSpec with MustMatchers with BeforeAndAfterEach { + + val tf = Duration.timeFactor + + override def beforeEach { + val f = Duration.getClass.getDeclaredField("timeFactor") + f.setAccessible(true) + f.setDouble(Duration, 2.0) + } + + override def afterEach { + val f = Duration.getClass.getDeclaredField("timeFactor") + f.setAccessible(true) + f.setDouble(Duration, tf) + } + + "A TestKit" must { + + "correctly dilate times" in { + val probe = TestProbe() + val now = System.nanoTime + intercept[AssertionError] { probe.awaitCond(false, Duration("1 second")) } + val diff = System.nanoTime - now + diff must be > 1700000000l + diff must be < 3000000000l + } + + } + +}