From 2c5370f7bac451b4acbbabb10b5d589aa99d0caf Mon Sep 17 00:00:00 2001 From: Viktor Klang Date: Wed, 10 Apr 2013 00:59:50 +0200 Subject: [PATCH] #3198 - Making CoronerSpec taggedAs TimingTest --- .../src/main/scala/akka/actor/Actor.scala | 2 +- .../src/test/scala/akka/testkit/Coroner.scala | 61 ++++++++----------- .../test/scala/akka/testkit/CoronerSpec.scala | 18 +++--- 3 files changed, 33 insertions(+), 48 deletions(-) diff --git a/akka-actor/src/main/scala/akka/actor/Actor.scala b/akka-actor/src/main/scala/akka/actor/Actor.scala index 288045804c..d6cbc54051 100644 --- a/akka-actor/src/main/scala/akka/actor/Actor.scala +++ b/akka-actor/src/main/scala/akka/actor/Actor.scala @@ -369,7 +369,7 @@ object Actor { * `context`. The only abstract method is `receive` which shall return the * initial behavior of the actor as a partial function (behavior can be changed * using `context.become` and `context.unbecome`). - * + * * This is the Scala API (hence the Scala code below), for the Java API see [[akka.actor.UntypedActor]]. * * {{{ diff --git a/akka-testkit/src/test/scala/akka/testkit/Coroner.scala b/akka-testkit/src/test/scala/akka/testkit/Coroner.scala index 3207d08fa0..39de089e8b 100644 --- a/akka-testkit/src/test/scala/akka/testkit/Coroner.scala +++ b/akka-testkit/src/test/scala/akka/testkit/Coroner.scala @@ -9,6 +9,7 @@ import java.util.Date import java.util.concurrent.CountDownLatch import org.scalatest.{ BeforeAndAfterAll, Suite } import scala.annotation.tailrec +import scala.concurrent.{ Awaitable, CanAwait, Await } import scala.concurrent.duration._ import scala.util.control.NonFatal @@ -28,8 +29,9 @@ object Coroner { /** * Used to cancel the Coroner after calling `watch`. + * The result of this Awaitable will be `true` if it has been cancelled. */ - trait WatchHandle { + trait WatchHandle extends Awaitable[Boolean] { def cancel(): Unit } @@ -38,44 +40,29 @@ object Coroner { * The returned handle can be used to perform the cancellation. */ def watch(deadline: Deadline, reportTitle: String, out: PrintStream): WatchHandle = { + val cancelLatch = new CountDownLatch(1) with WatchHandle { + override def cancel(): Unit = countDown() + override def ready(atMost: Duration)(implicit permit: CanAwait): this.type = { + result(atMost) + this + } + override def result(atMost: Duration)(implicit permit: CanAwait): Boolean = await(atMost.length, atMost.unit) + } + + def triggerReportIfOverdue(duration: Duration): Unit = + if (!Await.result(cancelLatch, duration)) { + out.println(s"Coroner not cancelled after ${duration.toMillis}ms. Looking for signs of foul play...") + try printReport(reportTitle, out) catch { + case NonFatal(ex) ⇒ { + out.println("Error displaying Coroner's Report") + ex.printStackTrace(out) + } + } finally out.flush() + } val duration = deadline.timeLeft // Store for later reporting - val cancelLatch = new CountDownLatch(1) - - @tailrec def watchLoop() { - if (deadline.isOverdue) { - triggerReport() - } else { - val cancelled = try { - cancelLatch.await(deadline.timeLeft.length, deadline.timeLeft.unit) - } catch { - case _: InterruptedException ⇒ false - } - if (cancelled) { - // Our job is finished, let the thread stop - } else { - watchLoop() - } - } - } - - def triggerReport() { - out.println(s"Coroner not cancelled after ${duration.toMillis}ms. Looking for signs of foul play...") - try { - printReport(reportTitle, out) - } catch { - case NonFatal(ex) ⇒ { - out.println("Error displaying Coroner's Report") - ex.printStackTrace(out) - } - } - } - - val thread = new Thread(new Runnable { def run = watchLoop() }, "Coroner") + val thread = new Thread(new Runnable { def run = triggerReportIfOverdue(duration) }, "Coroner") thread.start() // Must store thread in val to work around SI-7203 - - new WatchHandle { - def cancel() { cancelLatch.countDown() } - } + cancelLatch } /** diff --git a/akka-testkit/src/test/scala/akka/testkit/CoronerSpec.scala b/akka-testkit/src/test/scala/akka/testkit/CoronerSpec.scala index fb0ffa9222..25ae910158 100644 --- a/akka-testkit/src/test/scala/akka/testkit/CoronerSpec.scala +++ b/akka-testkit/src/test/scala/akka/testkit/CoronerSpec.scala @@ -10,33 +10,31 @@ import java.util.concurrent.locks.ReentrantLock import org.scalatest.WordSpec import org.scalatest.matchers.MustMatchers import scala.concurrent.duration._ +import scala.concurrent.Await @org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner]) class CoronerSpec extends WordSpec with MustMatchers { private def captureOutput[A](f: PrintStream ⇒ A): (A, String) = { val bytes = new ByteArrayOutputStream() - val out = new PrintStream(bytes) + val out = new PrintStream(bytes, true, "UTF-8") val result = f(out) - (result, new String(bytes.toByteArray())) + (result, new String(bytes.toByteArray(), "UTF-8")) } "A Coroner" must { - "generate a report if enough time passes" in { - val (_, report) = captureOutput(out ⇒ { - val handle = Coroner.watch(500.milliseconds.fromNow, "XXXX", out) - Thread.sleep(1000.milliseconds.toMillis) - }) + "generate a report if enough time passes" taggedAs TimingTest in { + val (_, report) = captureOutput(out ⇒ Await.ready(Coroner.watch(100.milliseconds.fromNow, "XXXX", out), 1.second)) report must include("Coroner's Report") report must include("XXXX") } - "not generate a report if cancelled early" in { + "not generate a report if cancelled early" taggedAs TimingTest in { val (_, report) = captureOutput(out ⇒ { - val coroner = Coroner.watch(500.milliseconds.fromNow, "XXXX", out) + val coroner = Coroner.watch(100.milliseconds.fromNow, "XXXX", out) coroner.cancel() - Thread.sleep(1000.milliseconds.toMillis) + Await.ready(coroner, 1.second) }) report must be("") }