Skip to content

Instantly share code, notes, and snippets.

@bfritz
Last active January 27, 2017 00:18
Show Gist options
  • Save bfritz/544a56352f5fcdaf7522c09a24a791cc to your computer and use it in GitHub Desktop.
Save bfritz/544a56352f5fcdaf7522c09a24a791cc to your computer and use it in GitHub Desktop.
specs2 thread dump on test timeout
name := "specs2-thread-dump-on-timeout"
scalaVersion := "2.12.1"
val specs2Version = "3.8.6"
libraryDependencies ++= Seq(
"org.specs2" %% "specs2-core" % specs2Version,
"org.specs2" %% "specs2-matcher-extra" % specs2Version
)
package com.bfritz
package specs2
import scala.collection.JavaConverters._
import org.specs2.concurrent.ExecutionEnv
import org.specs2.control.Debug._
import org.specs2.execute.AsResult
import org.specs2.matcher._
import org.specs2.mutable.{Around, Specification}
import org.specs2.specification.Scope
import scala.concurrent.duration._
class WhyYouTimeoutSpec extends Specification {
"interrupt blocking sleep()" >> new MonitoredContext {
Thread.sleep(5000)
ok
}
trait MonitoredContext extends Scope with ThreadDumpOnTimeout
}
trait ThreadDumpOnTimeout extends Around with TerminationMatchers {
implicit val executionEnv = ExecutionEnv.fromGlobalExecutionContext
def dumpOnTimeout(s: String, timeout: Duration): String = {
def expandFrames(frames: Array[StackTraceElement]) = frames.map(f => s" $f\n").mkString
val dumpedThreads = for {
e <- Thread.getAllStackTraces().asScala
} yield s"${e._1}\n${expandFrames(e._2)}"
s"Timeout after $timeout with thread dump:\n\n${dumpedThreads.mkString("\n\n")}".pp
s"Timeout after $timeout"
}
override def around[T: AsResult](t: => T) = {
val timeout: Duration = 100.millis
// "before".pp
lazy val result = t
val termination = terminate(retries = 1, sleep = timeout).orSkip(dumpOnTimeout(_, timeout))(createExpectable(t))
// "after".pp
if (!termination.toResult.isSkipped) AsResult(result)
else termination.toResult
}
}
@bfritz
Copy link
Author

bfritz commented Jan 27, 2017

And a hacky way to error on timeout:

override def around[T: AsResult](t: => T) = {
    ...
    if (!termination.toResult.isSkipped) AsResult(result)
    else Error(termination.message, new NoTrace)
  }

  class NoTrace extends RuntimeException {
    override def fillInStackTrace(): Throwable = null
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment