Skip to content

Commit

Permalink
fix: #822 catch fatal errors in executions
Browse files Browse the repository at this point in the history
to avoid hanging threads
  • Loading branch information
symbiont-eric-torreborre committed Apr 3, 2020
1 parent 1f1efaf commit 93075d4
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import control.Property
import reflect.ClassName._
import text.NotNullStrings._
import java.util.regex.Pattern
import scala.util.control.NonFatal

/**
* This trait executes a Result and returns an appropriate value when a specs2 exception is thrown
Expand Down Expand Up @@ -33,7 +34,7 @@ trait ResultExecution { outer =>
case e: AssertionError => Error(e)
case e: java.lang.Error if simpleClassName(e) == "NotImplementedError" => Failure(e.getMessage.notNull, "", e.getStackTrace.toList, details = FromJUnitAssertionError)
case e: java.lang.Error if simpleClassName(e) == "ExpectationError" => Failure(e.toString, "", e.getStackTrace.toList, details = FromExpectationError)
case t: Exception => Error(t)
case NonFatal(t) => Error(t)
}

/** execute a Result and rethrow any exception or throws an exception if it is not a success */
Expand All @@ -60,7 +61,7 @@ trait ResultExecution { outer =>
case e: AssertionError => throw ErrorException(Error(e))
case e: java.lang.Error if simpleClassName(e) == "NotImplementedError" => throw FailureException(Failure(e.getMessage.notNull, "", e.getStackTrace.toList, details = FromJUnitAssertionError))
case e: java.lang.Error if simpleClassName(e) == "ExpectationError" => throw FailureException(Failure(e.toString, "", e.getStackTrace.toList, details = FromExpectationError))
case t: Exception => throw ErrorException(Error(t))
case NonFatal(t) => throw ErrorException(Error(t))
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ class ExecutionSpec(val env: Env) extends Specification with OwnEnv { def is = s
A link is executed by getting the corresponding specification ref status in the Statistics store
the Stats is the stats of the spec + specs += 1 $linkExecution

An execution can be created from a result throwing a FailureException $withFailureException
An execution can be created from a result throwing a FailureException
it will then create a failed result $withFailureException

An execution can be created from a result throwing a fatal exception
it will then throw an ExecutionException exception $withFatalException

"""

Expand All @@ -35,6 +39,10 @@ class ExecutionSpec(val env: Env) extends Specification with OwnEnv { def is = s
Execution.withEnv(_ => {throw new FailureException(failure); success}).result(env) === failure
}

def withFatalException = {
Execution.withEnv(_ => {throw new java.lang.NoSuchMethodError("boom"); success}).result(env) must throwAn[ExecutionException]
}

/**
* HELPERS
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,14 @@ case class Execution(run: Option[Env => Future[() => Result]] = None,
env.setContextClassLoader()

val timer = startSimpleTimer

val timedFuture = TimedFuture({ es =>
r(env).map(action => (action(), timer.stop))
r(env).flatMap { action =>
try Future.successful((action(), timer.stop))
catch { case t: Throwable =>
Future.failed(t)
}
}
}, to)

val future = timedFuture.runNow(env.executorServices).recoverWith { case e: FailureException =>
Expand Down

0 comments on commit 93075d4

Please sign in to comment.