summaryrefslogtreecommitdiff
path: root/src/partest
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2010-12-28 04:09:25 +0000
committerPaul Phillips <paulp@improving.org>2010-12-28 04:09:25 +0000
commit5f32d546950f55e31bf380009aff5538d630afaf (patch)
treec7c5769d654b949585c473d5c5ac9d669373ab3d /src/partest
parent09d502f2860481687bcb473cdc7fd489439af478 (diff)
downloadscala-5f32d546950f55e31bf380009aff5538d630afaf.tar.gz
scala-5f32d546950f55e31bf380009aff5538d630afaf.tar.bz2
scala-5f32d546950f55e31bf380009aff5538d630afaf.zip
The partest hangs are back in force.
down the long and freezy road once again. With this patch you can send a SIGHUP to partest and it will spew a bunch of internal state. It is also possible I fixed the underlying issue by cleaning up the super fragile dependence on counters never getting the least bit off track. If fixed, it'll still be fun to send signals. If not, this will be coming in handy reeeeeal soon. No review.
Diffstat (limited to 'src/partest')
-rw-r--r--src/partest/scala/tools/partest/nest/DirectRunner.scala57
-rw-r--r--src/partest/scala/tools/partest/nest/Worker.scala102
2 files changed, 91 insertions, 68 deletions
diff --git a/src/partest/scala/tools/partest/nest/DirectRunner.scala b/src/partest/scala/tools/partest/nest/DirectRunner.scala
index cca7804b9b..d15f851ffc 100644
--- a/src/partest/scala/tools/partest/nest/DirectRunner.scala
+++ b/src/partest/scala/tools/partest/nest/DirectRunner.scala
@@ -8,13 +8,13 @@
package scala.tools.partest
package nest
-import java.io.{File, PrintStream, FileOutputStream, BufferedReader,
- InputStreamReader, StringWriter, PrintWriter}
+import java.io.{ File }
import java.util.StringTokenizer
import scala.util.Properties.{ setProp }
+import scala.tools.util.Signallable
import scala.tools.nsc.util.ScalaClassLoader
import scala.tools.nsc.io.Directory
-
+import scala.collection.{ mutable, immutable }
import scala.actors.Actor._
import scala.actors.TIMEOUT
@@ -36,12 +36,28 @@ trait DirectRunner {
}
}
- def runTestsForFiles(_kindFiles: List[File], kind: String): scala.collection.immutable.Map[String, Int] = {
+ /** These things, formerly inside runTestsForFiles, have been promoted
+ * into private fields so I can inspect them via signal when partest shows
+ * signs of dementia.
+ */
+ private var workers: List[Worker] = Nil
+ private var logsToDelete: List[LogFile] = Nil
+ private var outdirsToDelete: List[File] = Nil
+ private val results = new mutable.HashMap[String, Int]()
+ private def addResults(kvs: Traversable[(String, Int)]) = synchronized { results ++= kvs }
+ private val signallable = Signallable("HUP", "Make partest dump its state.")(dumpState())
+
+ def dumpState() {
+ println("Dumping partest internals.")
+ println("results.size = " + results.size + ", " + workers.size + " workers.")
+ workers foreach println
+ }
+
+ def runTestsForFiles(_kindFiles: List[File], kind: String): immutable.Map[String, Int] = {
/** NO DUPLICATES, or partest will blow the count and hang forever. **/
val kindFiles = _kindFiles.distinct
- val len = kindFiles.length
- val (testsEach, lastFrag) = (len/numActors, len%numActors)
- val last = numActors-1
+ val groupSize = (kindFiles.length / numActors) + 1
+
val consFM = new ConsoleFileManager
import consFM.{ latestCompFile, latestLibFile, latestPartestFile }
val scalacheckURL = PathSettings.scalaCheck.toURL
@@ -49,44 +65,37 @@ trait DirectRunner {
List(scalacheckURL, latestCompFile.toURI.toURL, latestLibFile.toURI.toURL, latestPartestFile.toURI.toURL)
)
Output.init
- val workers = for (i <- List.range(0, numActors)) yield {
- val toTest = kindFiles.slice(i*testsEach, (i+1)*testsEach)
+
+ this.workers = kindFiles.grouped(groupSize).toList map { toTest =>
val worker = new Worker(fileManager, TestRunParams(scalaCheckParentClassLoader))
worker.start()
- if (i == last)
- worker ! RunTests(kind, (kindFiles splitAt (last*testsEach))._2)
- else
- worker ! RunTests(kind, toTest)
+ worker ! RunTests(kind, toTest)
worker
}
- var logsToDelete: List[File] = List()
- var outdirsToDelete: List[File] = List()
- var results = new scala.collection.immutable.HashMap[String, Int]
workers foreach { w =>
receiveWithin(3600 * 1000) {
case Results(res, logs, outdirs) =>
- logsToDelete :::= logs filter (_.toDelete)
- outdirsToDelete :::= outdirs
- results ++= res
+ logsToDelete ++= (logs filter (_.toDelete))
+ outdirsToDelete ++= outdirs
+ addResults(res)
case TIMEOUT =>
// add at least one failure
NestUI.verbose("worker timed out; adding failed test")
- results += ("worker timed out; adding failed test" -> 2)
+ addResults(Seq(("worker timed out; adding failed test" -> 2)))
}
}
if (isPartestDebug)
fileManager.showTestTimings()
-
- if (!isPartestDebug) {
- for (x <- logsToDelete ::: outdirsToDelete) {
+ else {
+ for (x <- logsToDelete ++ outdirsToDelete) {
NestUI.verbose("deleting "+x)
Directory(x).deleteRecursively()
}
}
- results
+ results.toMap
}
}
diff --git a/src/partest/scala/tools/partest/nest/Worker.scala b/src/partest/scala/tools/partest/nest/Worker.scala
index 8d4a8f1a5b..293de5c427 100644
--- a/src/partest/scala/tools/partest/nest/Worker.scala
+++ b/src/partest/scala/tools/partest/nest/Worker.scala
@@ -23,6 +23,7 @@ import scala.actors.{ Actor, Exit, TIMEOUT }
import scala.actors.Actor._
import scala.tools.scalap.scalax.rules.scalasig.{ByteCode, ClassFileParser, ScalaSigAttributeParsers}
+import scala.collection.{ mutable, immutable }
import scala.collection.immutable.{ HashMap, Map => ImmMap }
import scala.collection.Map
@@ -115,6 +116,30 @@ class Worker(val fileManager: FileManager, params: TestRunParams) extends Actor
else
fileManager.JAVAC_CMD
+ /** Formerly deeper inside, these next few things are now promoted outside so
+ * I can see what they're doing when the world comes to a premature stop.
+ */
+ private val filesRemaining = new mutable.HashSet[File]
+ private def addFilesRemaining(xs: Traversable[File]) = filesRemaining ++= xs
+ private def getNextFile() = synchronized {
+ val file = filesRemaining.head
+ filesRemaining -= file
+ file
+ }
+ // maps canonical file names to the test result (0: OK, 1: FAILED, 2: TIMOUT)
+ private val status = new mutable.HashMap[String, Int]
+ private def updateStatus(key: String, num: Int) = synchronized {
+ status(key) = num
+ }
+ override def toString = (
+ ">> Partest Worker:\n" +
+ "TestRunParams = " + params + "\n" +
+ filesRemaining.size + " files remaining:\n" +
+ filesRemaining.toList.sortBy(_.toString).map(" " + _ + "\n").mkString("") +
+ "\nstatus hashmap contains " + status.size + " entries:\n" +
+ status.toList.map(x => " " + x._1 + " -> " + x._2).sorted.mkString("\n") + "\n"
+ )
+
def workerError(msg: String): Unit = reporter.error(
FakePos("scalac"),
msg + "\n scalac -help gives more information"
@@ -1023,8 +1048,7 @@ class Worker(val fileManager: FileManager, params: TestRunParams) extends Actor
try {
// delete log file only if test was successful
- if (good && !logFile.isEmpty && !isPartestDebug)
- logFile.get.toDelete = true
+ logFile filter (_ => good && !isPartestDebug) foreach (_.toDelete = true)
writers match {
case Some((swr, wr)) =>
@@ -1046,64 +1070,54 @@ class Worker(val fileManager: FileManager, params: TestRunParams) extends Actor
}
}
- val numFiles = files.size
- if (numFiles == 0)
- reportAll(ImmMap(), topcont)
-
- // maps canonical file names to the test result (0: OK, 1: FAILED, 2: TIMOUT)
- var status = new HashMap[String, Int]
+ if (files.isEmpty) reportAll(ImmMap(), topcont)
+ else addFilesRemaining(files)
- var fileCnt = 1
- Actor.loopWhile(fileCnt <= numFiles) {
+ Actor.loopWhile(filesRemaining.nonEmpty) {
val parent = self
actor {
- val testFile = files(fileCnt-1)
-
+ val testFile = getNextFile()
val ontimeout = new TimerTask {
def run() = parent ! Timeout(testFile)
}
timer.schedule(ontimeout, fileManager.timeout.toLong)
- val context = try {
- processSingleFile(testFile)
- } catch {
- case t: Throwable =>
- NestUI.verbose("while invoking compiler ("+files+"):")
- NestUI.verbose("caught "+t)
- t.printStackTrace
- if (t.getCause != null)
- t.getCause.printStackTrace
- LogContext(null, None)
- }
+ val context =
+ try processSingleFile(testFile)
+ catch {
+ case t: Throwable =>
+ NestUI.verbose("while invoking compiler ("+files+"):")
+ NestUI.verbose("caught "+t)
+ t.printStackTrace
+ if (t.getCause != null)
+ t.getCause.printStackTrace
+ LogContext(null, None)
+ }
parent ! Result(testFile, context)
}
react {
case res: TestResult =>
val path = res.file.getCanonicalPath
- status.get(path) match {
- case Some(stat) => // ignore message
- case None =>
- res match {
- case Timeout(_) =>
- status = status + (path -> 2)
- val swr = new StringWriter
- val wr = new PrintWriter(swr)
- printInfoStart(res.file, wr)
- succeeded = false
- reportResult(2, None, Some((swr, wr)))
- case Result(_, logs) =>
- status = status + (path -> (if (succeeded) 0 else 1))
- reportResult(
- if (succeeded) 0 else 1,
- if (logs != null) Some(logs.file) else None,
- if (logs != null) logs.writers else None)
- }
- if (fileCnt == numFiles)
- reportAll(status, topcont)
- fileCnt += 1
+ if (status contains path) () // ignore message
+ else res match {
+ case Timeout(_) =>
+ updateStatus(path, 2)
+ val swr = new StringWriter
+ val wr = new PrintWriter(swr)
+ printInfoStart(res.file, wr)
+ succeeded = false
+ reportResult(2, None, Some((swr, wr)))
+ case Result(_, logs) =>
+ updateStatus(path, (if (succeeded) 0 else 1))
+ reportResult(
+ if (succeeded) 0 else 1,
+ if (logs != null) Some(logs.file) else None,
+ if (logs != null) logs.writers else None)
}
+ if (filesRemaining.isEmpty)
+ reportAll(status.toMap, topcont)
}
}
}