Last active
January 27, 2017 00:18
-
-
Save bfritz/544a56352f5fcdaf7522c09a24a791cc to your computer and use it in GitHub Desktop.
specs2 thread dump on test timeout
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
And a hacky way to error on timeout: