Add ability to timeout ClientTest cases

It is possible for the test cases in ClientTest to block indefinitely.
To avoid that, we can instead run them on a background thread and cancel
the thread if that happens.
This commit is contained in:
Ethan Atkins 2020-09-24 17:33:27 -07:00
parent e3731994de
commit 8a23f1e440
2 changed files with 40 additions and 15 deletions

View File

@ -59,6 +59,7 @@ import Serialization.{
setTerminalAttributes,
}
import NetworkClient.Arguments
import java.util.concurrent.TimeoutException
trait ConsoleInterface {
def appendLog(level: Level.Value, message: => String): Unit
@ -799,7 +800,10 @@ class NetworkClient(
val json = s"""{"query":"$query","level":1}"""
val execId = sendJson("sbt/completion", json)
pendingCompletions.put(execId, result.put)
val response = result.poll(30, TimeUnit.SECONDS)
val response = result.poll(30, TimeUnit.SECONDS) match {
case null => throw new TimeoutException("no response from server within 30 seconds")
case r => r
}
def fillCompletions(label: String, regex: String, command: String): Seq[String] = {
def updateCompletions(): Seq[String] = {
errorStream.println()

View File

@ -8,6 +8,7 @@
package testpkg
import java.io.{ InputStream, OutputStream, PrintStream }
import java.util.concurrent.{ LinkedBlockingQueue, TimeUnit, TimeoutException }
import sbt.internal.client.NetworkClient
import sbt.internal.util.Util
import scala.collection.mutable
@ -41,27 +42,47 @@ object ClientTest extends AbstractServerTest {
} else -1
}
}
private def client(args: String*) =
NetworkClient.client(
testPath.toFile,
args.toArray,
NullInputStream,
NullPrintStream,
NullPrintStream,
false
private[this] def background[R](f: => R): R = {
val result = new LinkedBlockingQueue[R]
val thread = new Thread("client-bg-thread") {
setDaemon(true)
start()
override def run(): Unit = result.put(f)
}
result.poll(1, TimeUnit.MINUTES) match {
case null =>
thread.interrupt()
thread.join(5000)
throw new TimeoutException
case r => r
}
}
private def client(args: String*): Int = {
background(
NetworkClient.client(
testPath.toFile,
args.toArray,
NullInputStream,
NullPrintStream,
NullPrintStream,
false
)
)
}
// This ensures that the completion command will send a tab that triggers
// sbt to call definedTestNames or discoveredMainClasses if there hasn't
// been a necessary compilation
def tabs = new FixedInputStream('\t', '\t')
private def complete(completionString: String): Seq[String] = {
val cps = new CachingPrintStream
NetworkClient.complete(
testPath.toFile,
Array(s"--completions=sbtn $completionString"),
false,
tabs,
cps
background(
NetworkClient.complete(
testPath.toFile,
Array(s"--completions=sbtn $completionString"),
false,
tabs,
cps
)
)
cps.lines
}