mirror of https://github.com/sbt/sbt.git
Add timeout to scripted statements
Sometimes scripted tasks hang in ci and they are effectively impossible to debug. To workaround this, I add a five minute timeout to any scripted test and print the log if the test fails.
This commit is contained in:
parent
d05e6580af
commit
155526fb11
|
|
@ -8,8 +8,10 @@
|
||||||
package sbt
|
package sbt
|
||||||
package scriptedtest
|
package scriptedtest
|
||||||
|
|
||||||
import scala.collection.mutable
|
import java.util.concurrent.{ Executors, TimeUnit, TimeoutException }
|
||||||
|
|
||||||
|
import scala.collection.mutable
|
||||||
|
import scala.concurrent.duration._
|
||||||
import sbt.internal.scripted._
|
import sbt.internal.scripted._
|
||||||
|
|
||||||
private[sbt] object BatchScriptRunner {
|
private[sbt] object BatchScriptRunner {
|
||||||
|
|
@ -17,8 +19,9 @@ private[sbt] object BatchScriptRunner {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Defines an alternative script runner that allows batch execution. */
|
/** Defines an alternative script runner that allows batch execution. */
|
||||||
private[sbt] class BatchScriptRunner extends ScriptRunner {
|
private[sbt] class BatchScriptRunner extends ScriptRunner with AutoCloseable {
|
||||||
import BatchScriptRunner.States
|
import BatchScriptRunner.States
|
||||||
|
private[this] val service = Executors.newCachedThreadPool()
|
||||||
|
|
||||||
/** Defines a method to run batched execution.
|
/** Defines a method to run batched execution.
|
||||||
*
|
*
|
||||||
|
|
@ -40,26 +43,35 @@ private[sbt] class BatchScriptRunner extends ScriptRunner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val timeout = 5.minutes
|
||||||
def processStatement(handler: StatementHandler, statement: Statement, states: States): Unit = {
|
def processStatement(handler: StatementHandler, statement: Statement, states: States): Unit = {
|
||||||
val state = states(handler).asInstanceOf[handler.State]
|
val state = states(handler).asInstanceOf[handler.State]
|
||||||
val nextState =
|
val nextStateFuture = service.submit(
|
||||||
try Right(handler(statement.command, statement.arguments, state))
|
() =>
|
||||||
catch { case e: Exception => Left(e) }
|
try Right(handler(statement.command, statement.arguments, state))
|
||||||
nextState match {
|
catch { case e: Exception => Left(e) }
|
||||||
case Left(err) =>
|
)
|
||||||
if (statement.successExpected) {
|
try {
|
||||||
err match {
|
nextStateFuture.get(timeout.toMillis, TimeUnit.MILLISECONDS) match {
|
||||||
case t: TestFailed =>
|
case Left(err) =>
|
||||||
throw new TestException(statement, "Command failed: " + t.getMessage, null)
|
if (statement.successExpected) {
|
||||||
case _ => throw new TestException(statement, "Command failed", err)
|
err match {
|
||||||
}
|
case t: TestFailed =>
|
||||||
} else
|
throw new TestException(statement, "Command failed: " + t.getMessage, null)
|
||||||
()
|
case _ => throw new TestException(statement, "Command failed", err)
|
||||||
case Right(s) =>
|
}
|
||||||
if (statement.successExpected)
|
} else
|
||||||
states(handler) = s
|
()
|
||||||
else
|
case Right(s) =>
|
||||||
throw new TestException(statement, "Command succeeded but failure was expected", null)
|
if (statement.successExpected)
|
||||||
|
states(handler) = s
|
||||||
|
else
|
||||||
|
throw new TestException(statement, "Command succeeded but failure was expected", null)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
case e: TimeoutException => throw new TestException(statement, "Command timed out", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override def close(): Unit = service.shutdown()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,8 @@ final class ScriptedTests(
|
||||||
createScriptedHandlers(testDirectory, buffer, RemoteSbtCreatorKind.LauncherBased)
|
createScriptedHandlers(testDirectory, buffer, RemoteSbtCreatorKind.LauncherBased)
|
||||||
val runner = new BatchScriptRunner
|
val runner = new BatchScriptRunner
|
||||||
val states = new mutable.HashMap[StatementHandler, StatementHandler#State]()
|
val states = new mutable.HashMap[StatementHandler, StatementHandler#State]()
|
||||||
commonRunTest(label, testDirectory, prescripted, handlers, runner, states, buffer)
|
try commonRunTest(label, testDirectory, prescripted, handlers, runner, states, buffer)
|
||||||
|
finally runner.close()
|
||||||
}
|
}
|
||||||
runOrHandleDisabled(label, testDirectory, singleTestRunner, buffer)
|
runOrHandleDisabled(label, testDirectory, singleTestRunner, buffer)
|
||||||
}
|
}
|
||||||
|
|
@ -362,7 +363,10 @@ final class ScriptedTests(
|
||||||
}
|
}
|
||||||
|
|
||||||
try runBatchTests
|
try runBatchTests
|
||||||
finally runner.cleanUpHandlers(seqHandlers, states)
|
finally {
|
||||||
|
runner.cleanUpHandlers(seqHandlers, states)
|
||||||
|
runner.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def runOrHandleDisabled(
|
private def runOrHandleDisabled(
|
||||||
|
|
@ -415,6 +419,7 @@ final class ScriptedTests(
|
||||||
case null | _: SocketException => log.error(s" Cause of test exception: ${t.getMessage}")
|
case null | _: SocketException => log.error(s" Cause of test exception: ${t.getMessage}")
|
||||||
case _ => t.printStackTrace()
|
case _ => t.printStackTrace()
|
||||||
}
|
}
|
||||||
|
log.play()
|
||||||
}
|
}
|
||||||
if (pending) None else Some(label)
|
if (pending) None else Some(label)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue