#3198 - Making CoronerSpec taggedAs TimingTest

This commit is contained in:
Viktor Klang 2013-04-10 00:59:50 +02:00
parent e7083dcac3
commit 2c5370f7ba
3 changed files with 33 additions and 48 deletions

View file

@ -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]].
*
* {{{

View file

@ -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
}
/**

View file

@ -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("")
}