summaryrefslogtreecommitdiff
path: root/src/partest
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2010-04-06 22:46:22 +0000
committerPaul Phillips <paulp@improving.org>2010-04-06 22:46:22 +0000
commitd5b8082ce9269f6657d6d3b5467f57fa746f7e76 (patch)
tree687eb6276929ffc4e061cbced1f226dd232bfaab /src/partest
parent2a8667d1cd0649c5567c65b087f08f5d42e2062b (diff)
downloadscala-d5b8082ce9269f6657d6d3b5467f57fa746f7e76.tar.gz
scala-d5b8082ce9269f6657d6d3b5467f57fa746f7e76.tar.bz2
scala-d5b8082ce9269f6657d6d3b5467f57fa746f7e76.zip
Fixed another partest feature I'd managed to br...
Fixed another partest feature I'd managed to break at the very last minute. When a test is too slow finishing, there will be messages identifying the test. It defaults to 90 seconds before the first warning because I know some machines are slow, but it'd be nice if that was more like 30. No review.
Diffstat (limited to 'src/partest')
-rw-r--r--src/partest/scala/tools/partest/Alarms.scala85
-rw-r--r--src/partest/scala/tools/partest/Config.scala6
-rw-r--r--src/partest/scala/tools/partest/Dispatcher.scala57
-rw-r--r--src/partest/scala/tools/partest/Entities.scala1
-rw-r--r--src/partest/scala/tools/partest/PartestSpec.scala15
-rw-r--r--src/partest/scala/tools/partest/Universe.scala1
-rw-r--r--src/partest/scala/tools/partest/package.scala1
-rw-r--r--src/partest/scala/tools/partest/util/package.scala50
8 files changed, 109 insertions, 107 deletions
diff --git a/src/partest/scala/tools/partest/Alarms.scala b/src/partest/scala/tools/partest/Alarms.scala
new file mode 100644
index 0000000000..72afc232e5
--- /dev/null
+++ b/src/partest/scala/tools/partest/Alarms.scala
@@ -0,0 +1,85 @@
+/* NEST (New Scala Test)
+ * Copyright 2007-2010 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools
+package partest
+
+import java.util.{ Timer, TimerTask }
+
+trait Alarms {
+ self: Universe =>
+
+ def interruptMeIn[T](seconds: Int)(body: => T): Option[T] = {
+ val thisThread = currentThread
+ val alarm = new SimpleAlarm(seconds * 1000) set thisThread.interrupt()
+
+ try { Some(body) }
+ catch { case _: InterruptedException => None }
+ finally { alarm.cancel() ; Thread.interrupted() }
+ }
+
+ case class AlarmerAction(secs: Int, action: () => Unit) extends Runnable {
+ override def run() = action()
+ }
+
+ /** Set any number of alarms up with tuples of the form:
+ * seconds to alarm -> Function0[Unit] to execute
+ */
+ class Alarmer(alarms: AlarmerAction*) {
+ import java.util.concurrent._
+
+ val exec = Executors.newSingleThreadScheduledExecutor()
+ alarms foreach (x => exec.schedule(x, x.secs, TimeUnit.SECONDS))
+ exec.shutdown()
+
+ def cancelAll() = exec.shutdownNow()
+ }
+
+ class SimpleAlarm(timeout: Long) {
+ private val alarm = new Timer
+
+ /** Start a timer, running the given body if it goes off.
+ */
+ def set(body: => Unit) = returning(new TimerTask { def run() = body })(alarm.schedule(_, timeout))
+
+ /** Cancel the timer.
+ */
+ def cancel() = alarm.cancel()
+ }
+
+ trait TestAlarms {
+ test: TestEntity =>
+
+ private def warning1 = AlarmerAction(testWarning, () => warning(
+ """|I've been waiting %s seconds for this to complete:
+ | %s
+ |It may be stuck, or if not, it should be broken into smaller tests.
+ |""".stripMargin.format(testWarning, test))
+ )
+ private def warning2 = AlarmerAction(testWarning * 2, () => warning(
+ """|Now I've been waiting %s seconds for this to complete:
+ | %s
+ |If partest seems hung it would be a good place to look.
+ |""".stripMargin.format(testWarning * 2, test))
+ )
+
+ def startAlarms(onTimeout: => Unit) =
+ if (isNoAlarms) new Alarmer() // for alarm debugging
+ else new Alarmer(Seq(warning1, warning2, AlarmerAction(testTimeout, () => onTimeout)): _*)
+ }
+
+ // Thread.setDefaultUncaughtExceptionHandler(new UncaughtException)
+ // class UncaughtException extends Thread.UncaughtExceptionHandler {
+ // def uncaughtException(t: Thread, e: Throwable) {
+ // Console.println("Uncaught in %s: %s".format(t, e))
+ // }
+ // }
+ //
+ // lazy val logger = File("/tmp/partest.log").bufferedWriter()
+ // def flog(msg: String) = logger synchronized {
+ // logger write (msg + "\n")
+ // logger.flush()
+ // }
+}
diff --git a/src/partest/scala/tools/partest/Config.scala b/src/partest/scala/tools/partest/Config.scala
index 6be08ae8d2..8023574ae4 100644
--- a/src/partest/scala/tools/partest/Config.scala
+++ b/src/partest/scala/tools/partest/Config.scala
@@ -22,9 +22,9 @@ trait Config {
/** Values related to actors. The timeouts are in seconds. On a dry
* run we only allocate one worker so the output isn't interspersed.
*/
- def workerTimeout = 3600 // 1 hour, seems overly generous
- def testWarningSecs = testWarning.toInt // test warning - 90s by default
- def testTimeout = testWarningSecs * 10 // test timeout
+ def workerTimeout = 3600 // 1 hour, probably overly generous
+ def testTimeout = testTimeout_ flatMap safeToInt getOrElse 900 // test timeout
+ def testWarning = testWarning_ flatMap safeToInt getOrElse (testTimeout / 10) // test warning
def numWorkers = if (isDryRun) 1 else propOrElse("partest.actors", "8").toInt
def expectedErrors = propOrElse("partest.errors", "0").toInt
def poolSize = (wrapAccessControl(propOrNone("actors.corePoolSize")) getOrElse "16").toInt
diff --git a/src/partest/scala/tools/partest/Dispatcher.scala b/src/partest/scala/tools/partest/Dispatcher.scala
index e453e27293..2c7d9d6a2f 100644
--- a/src/partest/scala/tools/partest/Dispatcher.scala
+++ b/src/partest/scala/tools/partest/Dispatcher.scala
@@ -6,12 +6,11 @@
package scala.tools
package partest
-import util._
import scala.tools.nsc.io._
-import scala.actors.{ Actor, Exit, TIMEOUT }
+import scala.actors.{ Actor, TIMEOUT }
import scala.actors.Actor._
import scala.collection.immutable
-import scala.util.control.ControlThrowable
+import scala.util.control.Exception.ultimately
/** The machinery for concurrent execution of tests. Each Worker
* is given a bundle of tests, which it runs sequentially and then
@@ -140,52 +139,16 @@ trait Dispatcher {
Actor.loopWhile(testIterator.hasNext) {
val parent = self
- // pick a test
- val test = testIterator.next
-
- // Sets three alarms: two slowness warnings and a timeout.
- def setAlarms() =
- new Alarmer(
- testWarningSecs -> (() => warning(
- """|I've been waiting %d seconds for this to complete:
- | "%s"
- |Either it's stuck or it is unreasonably slow and should
- |be divided into smaller tests.
- |""".stripMargin.format(testWarning, test))),
-
- (testWarningSecs * 2) -> (() => warning(
- """|Now I've been waiting %d seconds for this to complete:
- | "%s"
- |If partest seems hung, let's blame that one.
- |""".stripMargin.format(testWarning * 2, test))),
-
- testTimeout -> (() => parent ! new Timeout(test))
- )
+ // pick a test and set some alarms
+ val test = testIterator.next
+ val alarmer = test startAlarms (parent ! new Timeout(test))
actor {
- /** Debugging alarm issues */
- if (isNoAlarms) {
- parent ! TestResult(test, test.isSuccess)
- }
- else {
- // Set alarms, kick it off. Calling isSuccess forces the lazy val
- // "process" inside the test, running it.
- val alarmer = setAlarms()
- def respondWith(res: TestResult) = {
- // Cancel the alarms and alert the media.
- alarmer.cancelAll()
- parent ! res
- }
-
- try respondWith(TestResult(test, test.isSuccess))
- catch {
- case x: ControlThrowable =>
- test.warnAndLogException("Worker caught " + x + ", rethrowing: ", x)
- throw x
- case x =>
- test.warnAndLogException("Worker caught " + x + ", failing: ", x)
- respondWith(TestResult(test, false))
- }
+ ultimately(alarmer.cancelAll()) {
+ // Calling isSuccess forces the lazy val "process" inside the test, running it.
+ val res = test.isSuccess
+ // Cancel the alarms and alert the media.
+ parent ! TestResult(test, res)
}
}
diff --git a/src/partest/scala/tools/partest/Entities.scala b/src/partest/scala/tools/partest/Entities.scala
index ad1a396902..ceed145148 100644
--- a/src/partest/scala/tools/partest/Entities.scala
+++ b/src/partest/scala/tools/partest/Entities.scala
@@ -14,6 +14,7 @@ trait Entities {
abstract class TestEntity extends AbsTestEntity
with TestContribution
with TestHousekeeping
+ with TestAlarms
with EntityLogging
with CompilableTest
with ScriptableTest
diff --git a/src/partest/scala/tools/partest/PartestSpec.scala b/src/partest/scala/tools/partest/PartestSpec.scala
index 55d2fdcca5..a1f7d4ef28 100644
--- a/src/partest/scala/tools/partest/PartestSpec.scala
+++ b/src/partest/scala/tools/partest/PartestSpec.scala
@@ -73,19 +73,20 @@ trait PartestSpec extends CommandLineSpec {
val isAnsi = "ansi" / "print output in color" ?
heading ("Other options:")
- val timeout = "timeout" / "Timeout in seconds" >> ;
- val isCleanup = "cleanup" / "delete all stale files and dirs before run" ?
- val isNoCleanup = "nocleanup" / "do not delete any logfiles or object dirs" ?
- val isStats = "stats" / "collect and print statistics about the tests" ?
- val isValidate = "validate" / "examine test filesystem for inconsistencies" ?
- val isVersion = "version" / "print version" ?
+ val timeout_ = "timeout" / "Overall timeout in seconds" |> "14400"
+ val testWarning_ = "test-warning" / "Test warning in seconds" >> ; // defaults to testTimeout / 10
+ val testTimeout_ = "test-timeout" / "Test timeout in seconds" >> ; // defaults to 900
+ val isCleanup = "cleanup" / "delete all stale files and dirs before run" ?
+ val isNoCleanup = "nocleanup" / "do not delete any logfiles or object dirs" ?
+ val isStats = "stats" / "collect and print statistics about the tests" ?
+ val isValidate = "validate" / "examine test filesystem for inconsistencies" ?
+ val isVersion = "version" / "print version" ?
// no help for anything below this line - secret options
// mostly intended for property configuration.
val runsets = "runsets" |> ""
val isNoAlarms = ("noalarms" ?)
val isInsideAnt = ("is-in-ant" ?)
- val testWarning = "test-warning" |> "90"
}
object PartestSpecReference extends PartestSpec {
diff --git a/src/partest/scala/tools/partest/Universe.scala b/src/partest/scala/tools/partest/Universe.scala
index 568a155281..557d48fe54 100644
--- a/src/partest/scala/tools/partest/Universe.scala
+++ b/src/partest/scala/tools/partest/Universe.scala
@@ -26,6 +26,7 @@ abstract class Universe
with PartestCompilation
with PartestSpec
with Config
+ with Alarms
with Actions
with Categories {
diff --git a/src/partest/scala/tools/partest/package.scala b/src/partest/scala/tools/partest/package.scala
index 78dd8d569c..3ef4db7cd8 100644
--- a/src/partest/scala/tools/partest/package.scala
+++ b/src/partest/scala/tools/partest/package.scala
@@ -18,6 +18,7 @@ package object partest {
private[partest] def safeLines(f: File) = safeSlurp(f) split """\r\n|\r|\n""" toList
private[partest] def safeArgs(f: File) = toArgs(safeSlurp(f))
+ private[partest] def safeToInt(s: String) = try Some(s.toInt) catch { case _: NumberFormatException => None }
private[partest] def isJava(f: Path) = f.isFile && (f hasExtension "java")
private[partest] def isScala(f: Path) = f.isFile && (f hasExtension "scala")
private[partest] def isJavaOrScala(f: Path) = isJava(f) || isScala(f)
diff --git a/src/partest/scala/tools/partest/util/package.scala b/src/partest/scala/tools/partest/util/package.scala
index d20b0659ec..bc5470ba5d 100644
--- a/src/partest/scala/tools/partest/util/package.scala
+++ b/src/partest/scala/tools/partest/util/package.scala
@@ -12,14 +12,6 @@ import nsc.io._
/** Misc code still looking for a good home.
*/
package object util {
- def interruptMeIn[T](seconds: Int)(body: => T): Option[T] = {
- val thisThread = currentThread
- val alarm = new SimpleAlarm(seconds * 1000) set thisThread.interrupt()
-
- try { Some(body) }
- catch { case _: InterruptedException => None }
- finally { alarm.cancel() ; Thread.interrupted() }
- }
def allPropertiesString() = javaHashtableToString(System.getProperties)
@@ -67,45 +59,3 @@ package object util {
if (result == "No differences") "" else result
}
}
-
-package util {
- /** Set any number of alarms up with tuples of the form:
- * seconds to alarm -> Function0[Unit] to execute
- */
- class Alarmer(alarmTimes: (Int, () => Unit)*) {
- import java.util.concurrent._
-
- val exec = Executors.newSingleThreadScheduledExecutor()
- private def sched(secs: Int, f: () => Unit) =
- exec.schedule(new Runnable { def run() = f() }, secs, TimeUnit.SECONDS)
-
- alarmTimes foreach (sched _).tupled
- exec.shutdown()
-
- def cancelAll() = exec.shutdownNow()
- }
-
- class SimpleAlarm(timeout: Long) {
- private val alarm = new Timer
- /** Start a timer, running the given body if it goes off.
- */
- def set(body: => Unit) = returning(new TimerTask { def run() = body })(alarm.schedule(_, timeout))
-
- /** Cancel the timer.
- */
- def cancel() = alarm.cancel()
- }
-
- // Thread.setDefaultUncaughtExceptionHandler(new UncaughtException)
- // class UncaughtException extends Thread.UncaughtExceptionHandler {
- // def uncaughtException(t: Thread, e: Throwable) {
- // Console.println("Uncaught in %s: %s".format(t, e))
- // }
- // }
- //
- // lazy val logger = File("/tmp/partest.log").bufferedWriter()
- // def flog(msg: String) = logger synchronized {
- // logger write (msg + "\n")
- // logger.flush()
- // }
-} \ No newline at end of file