#3198 - Making CoronerSpec taggedAs TimingTest
This commit is contained in:
parent
e7083dcac3
commit
2c5370f7ba
3 changed files with 33 additions and 48 deletions
|
|
@ -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]].
|
||||
*
|
||||
* {{{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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("")
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue