mirror of
https://github.com/sbt/sbt.git
synced 2026-08-29 01:24:09 +02:00
Fix race condition in non-forked, parallel tests.
Non forked tests that are run in parallel groups can call into a single instance of TestStatusReporter concurrently. This seems to be limited to the startGroup/endGroup/testEvent methods called in: https://github.com/sbt/sbt/blob/a41727fb17bb923036f90ca2ca62897def1a893e/testing/src/main/scala/sbt/TestFramework.scala#L100-L124 Which itself is called within: https://github.com/sbt/sbt/blob/a41727fb17bb923036f90ca2ca62897def1a893e/testing/src/main/scala/sbt/TestFramework.scala#L203-L229 Creating the `runnables` that are run in parallel (in builds so configured): https://github.com/sbt/sbt/blob/a6eb1260c8162bfbfcbfb8656f152854a93d33ae/main-actions/src/main/scala/sbt/Tests.scala#L222-L230 We believe this to be the cause of the hang witnessed in the a suite of Scalacheck-framework tests in the Scala build: https://github.com/scala/scala-jenkins-infra/issues/249 This commit uses a concurrent map to support concurrent status updates.
This commit is contained in:
@@ -63,7 +63,8 @@ private[sbt] object Server {
|
||||
val maxSocketLength = new UnixDomainSocketLibrary.SockaddrUn().sunPath.length - 1
|
||||
val path = socketfile.getAbsolutePath
|
||||
if (path.length > maxSocketLength)
|
||||
sys.error("socket file absolute path too long; " +
|
||||
sys.error(
|
||||
"socket file absolute path too long; " +
|
||||
"either switch to another connection type " +
|
||||
"or define a short \"SBT_GLOBAL_SERVER_DIR\" value. " +
|
||||
s"Current path: ${path}")
|
||||
|
||||
@@ -8,14 +8,16 @@
|
||||
package sbt
|
||||
|
||||
import java.io.File
|
||||
import sbt.io.IO
|
||||
|
||||
import scala.collection.mutable.Map
|
||||
import sbt.io.IO
|
||||
import sbt.protocol.testing.TestResult
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
import scala.collection.concurrent
|
||||
|
||||
// Assumes exclusive ownership of the file.
|
||||
private[sbt] class TestStatusReporter(f: File) extends TestsListener {
|
||||
private lazy val succeeded = TestStatus.read(f)
|
||||
private lazy val succeeded: concurrent.Map[String, Long] = TestStatus.read(f)
|
||||
|
||||
def doInit = ()
|
||||
def startGroup(name: String): Unit = { succeeded remove name }
|
||||
@@ -32,13 +34,16 @@ private[sbt] class TestStatusReporter(f: File) extends TestsListener {
|
||||
|
||||
private[sbt] object TestStatus {
|
||||
import java.util.Properties
|
||||
def read(f: File): Map[String, Long] = {
|
||||
def read(f: File): concurrent.Map[String, Long] = {
|
||||
import scala.collection.JavaConverters._
|
||||
val properties = new Properties
|
||||
IO.load(properties, f)
|
||||
properties.asScala map { case (k, v) => (k, v.toLong) }
|
||||
val result = new ConcurrentHashMap[String, Long]()
|
||||
properties.asScala.iterator.foreach { case (k, v) => result.put(k, v.toLong) }
|
||||
result.asScala
|
||||
}
|
||||
def write(map: Map[String, Long], label: String, f: File): Unit = {
|
||||
|
||||
def write(map: collection.Map[String, Long], label: String, f: File): Unit = {
|
||||
val properties = new Properties
|
||||
for ((test, lastSuccessTime) <- map)
|
||||
properties.setProperty(test, lastSuccessTime.toString)
|
||||
|
||||
Reference in New Issue
Block a user