Skip to content

Commit 155526f

Browse files
committed
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.
1 parent d05e658 commit 155526f

2 files changed

Lines changed: 39 additions & 22 deletions

File tree

scripted-sbt-redux/src/main/scala/sbt/scriptedtest/BatchScriptRunner.scala

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,20 @@
88
package sbt
99
package scriptedtest
1010

11-
import scala.collection.mutable
11+
import java.util.concurrent.{ Executors, TimeUnit, TimeoutException }
1212

13+
import scala.collection.mutable
14+
import scala.concurrent.duration._
1315
import sbt.internal.scripted._
1416

1517
private[sbt] object BatchScriptRunner {
1618
type States = mutable.HashMap[StatementHandler, StatementHandler#State]
1719
}
1820

1921
/** Defines an alternative script runner that allows batch execution. */
20-
private[sbt] class BatchScriptRunner extends ScriptRunner {
22+
private[sbt] class BatchScriptRunner extends ScriptRunner with AutoCloseable {
2123
import BatchScriptRunner.States
24+
private[this] val service = Executors.newCachedThreadPool()
2225

2326
/** Defines a method to run batched execution.
2427
*
@@ -40,26 +43,35 @@ private[sbt] class BatchScriptRunner extends ScriptRunner {
4043
}
4144
}
4245

46+
private val timeout = 5.minutes
4347
def processStatement(handler: StatementHandler, statement: Statement, states: States): Unit = {
4448
val state = states(handler).asInstanceOf[handler.State]
45-
val nextState =
46-
try Right(handler(statement.command, statement.arguments, state))
47-
catch { case e: Exception => Left(e) }
48-
nextState match {
49-
case Left(err) =>
50-
if (statement.successExpected) {
51-
err match {
52-
case t: TestFailed =>
53-
throw new TestException(statement, "Command failed: " + t.getMessage, null)
54-
case _ => throw new TestException(statement, "Command failed", err)
55-
}
56-
} else
57-
()
58-
case Right(s) =>
59-
if (statement.successExpected)
60-
states(handler) = s
61-
else
62-
throw new TestException(statement, "Command succeeded but failure was expected", null)
49+
val nextStateFuture = service.submit(
50+
() =>
51+
try Right(handler(statement.command, statement.arguments, state))
52+
catch { case e: Exception => Left(e) }
53+
)
54+
try {
55+
nextStateFuture.get(timeout.toMillis, TimeUnit.MILLISECONDS) match {
56+
case Left(err) =>
57+
if (statement.successExpected) {
58+
err match {
59+
case t: TestFailed =>
60+
throw new TestException(statement, "Command failed: " + t.getMessage, null)
61+
case _ => throw new TestException(statement, "Command failed", err)
62+
}
63+
} else
64+
()
65+
case Right(s) =>
66+
if (statement.successExpected)
67+
states(handler) = s
68+
else
69+
throw new TestException(statement, "Command succeeded but failure was expected", null)
70+
}
71+
} catch {
72+
case e: TimeoutException => throw new TestException(statement, "Command timed out", e)
6373
}
6474
}
75+
76+
override def close(): Unit = service.shutdown()
6577
}

scripted-sbt-redux/src/main/scala/sbt/scriptedtest/ScriptedTests.scala

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ final class ScriptedTests(
7272
createScriptedHandlers(testDirectory, buffer, RemoteSbtCreatorKind.LauncherBased)
7373
val runner = new BatchScriptRunner
7474
val states = new mutable.HashMap[StatementHandler, StatementHandler#State]()
75-
commonRunTest(label, testDirectory, prescripted, handlers, runner, states, buffer)
75+
try commonRunTest(label, testDirectory, prescripted, handlers, runner, states, buffer)
76+
finally runner.close()
7677
}
7778
runOrHandleDisabled(label, testDirectory, singleTestRunner, buffer)
7879
}
@@ -362,7 +363,10 @@ final class ScriptedTests(
362363
}
363364

364365
try runBatchTests
365-
finally runner.cleanUpHandlers(seqHandlers, states)
366+
finally {
367+
runner.cleanUpHandlers(seqHandlers, states)
368+
runner.close()
369+
}
366370
}
367371

368372
private def runOrHandleDisabled(
@@ -415,6 +419,7 @@ final class ScriptedTests(
415419
case null | _: SocketException => log.error(s" Cause of test exception: ${t.getMessage}")
416420
case _ => t.printStackTrace()
417421
}
422+
log.play()
418423
}
419424
if (pending) None else Some(label)
420425
}

0 commit comments

Comments
 (0)