diff options
Diffstat (limited to 'src/partest')
-rw-r--r-- | src/partest/scala/tools/partest/MasterActor.scala | 163 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/TestRunner.scala | 197 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/WorkerActor.scala | 307 | ||||
-rw-r--r-- | src/partest/scala/tools/partest/utils/PrintMgr.scala | 51 |
4 files changed, 718 insertions, 0 deletions
diff --git a/src/partest/scala/tools/partest/MasterActor.scala b/src/partest/scala/tools/partest/MasterActor.scala new file mode 100644 index 0000000000..77924a73b5 --- /dev/null +++ b/src/partest/scala/tools/partest/MasterActor.scala @@ -0,0 +1,163 @@ +/* __ *\ +** ________ ___ / / ___ Scala Parallel Testing ** +** / __/ __// _ | / / / _ | (c) 2007-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: $ + +package scala.tools.partest + +import java.awt.event.{ActionEvent, ActionListener} +import java.io.{File, FileOutputStream, PrintStream} +import javax.swing.Timer + +import scala.actors.Actor +import scala.tools.nsc.Settings + +import utils.PrintMgr._ + +/** + * @author Adriaan Moors, Thomas Hofer + * @version 1.0 + */ +class MasterActor(testDir: File, out: PrintStream) extends Actor { + import scala.actors.Actor._ + + private final val testPathLen = testDir.getPath.length + + private final val WIDTH = 56 + private final val TIMEOUT = 1360000 + + private var counter = 0 + private var failed = 0 + + private var conservative = false + + private val globalSettings = new Settings(x => ()) + + //private var numOfActors = Math.min(4, Math.max(Integer.parseInt(System.getProperty("actors.maxPoolSize")), + private val numOfActors = 4 + //println("Starting with " + numOfActors + " actors...") + + private var workers = (for (i <- 0 until numOfActors) yield (new WorkerActor(this, new Settings(x => ()), new ExtConsoleReporter(globalSettings)), i)).toList + + private var workingOn: List[(Int, Test)] = List() + + private var timers = (for (i <- 0 until numOfActors) yield createTimer(workers(i)._1)).toList + + private var testsToRun: List[Test] = List() + + private var failedTests: List[Test] = List() + + private def createTimer(worker: WorkerActor): Timer = { + val action: ActionListener = new ActionListener { + def actionPerformed(event: ActionEvent) { + val workerID = workers.find((_)._1 == worker) match { + case Some(x) => x + case None => (null, -1) + } + val test = workingOn.find((_)._1 == workerID._2) match { + case Some(x) => x._2 + case None => null + } + println("Actor " + workerID._1 + " failed, while testing " + test.file.getPath) + failedTests.find(_ == test) match { + case Some(x) => //... + case None => testFailed(workerID, test) + } + + } + } + + new Timer(TIMEOUT, action) + } + + private def testFailed(actor: (WorkerActor, Int), test: Test) = { + failedTests = test :: failedTests + var newWorker = new WorkerActor(this, new Settings(x => ()), new ExtConsoleReporter(globalSettings)) + timers(actor._2).stop + timers = timers.take(actor._2 - 1) ::: List(createTimer(newWorker)) ::: timers.drop(actor._2) + newWorker.start + //println("Started actor " + newWorker) + workers = (newWorker, actor._2) :: workers.remove(_ == actor) + + timers(actor._2).start + newWorker ! (test, true, conservative) + } + + private def hasNextTest = !testsToRun.isEmpty + + private def nextTest(): Test = { + val test = testsToRun.head + testsToRun = testsToRun.tail + test + } + + def act() { + loop { + react { + case (test: Test) => + testsToRun = test :: testsToRun + + case ("start", conservative: Boolean) => + this.conservative = conservative + workers foreach ((x) => { + if (hasNextTest) { + x._1.start + val test = nextTest() + // TODO Change here should be x._1 ! (test, false, conservative) + x._1 ! (test, false, conservative) + timers(x._2).start + workingOn = (x._2, test) :: workingOn + } + }) + + case (kind: String, succeeded: Boolean, file: File) => + val workerID = workers.find((_)._1 == sender) match { + case Some(x) => x + case None => (null, -1) + } + if (workerID._2 != -1) { + workingOn = workingOn.remove((_)._1 == workerID._2) + if (hasNextTest) { + val test = nextTest() + // TODO Change here should be x._1 ! (test, false, conservative) + sender ! (test, false, conservative) + timers(workerID._2).restart + workingOn = (workerID._2, test) :: workingOn + } else { + sender ! false + timers(workerID._2).stop + } + } else { + //Houston, we got a problem... + } + counter += 1 + printOutline("testing: ") + val name = file.getPath.substring(testPathLen) + print("[...]" + name + List.toString(List.make(WIDTH - name.length, ' ')) + "[") + if (succeeded) { + printSuccess(" OK ") + } else { + failed += 1 + printFailure("FAILED") + } + println("]") + if (workingOn.isEmpty) { + out.println(failed) + out.println(counter - failed) + out.close + println + exit + } + + case msg => + println(msg) + } + } + } + +} diff --git a/src/partest/scala/tools/partest/TestRunner.scala b/src/partest/scala/tools/partest/TestRunner.scala new file mode 100644 index 0000000000..1fa0680a9a --- /dev/null +++ b/src/partest/scala/tools/partest/TestRunner.scala @@ -0,0 +1,197 @@ +/* __ *\ +** ________ ___ / / ___ Scala Parallel Testing ** +** / __/ __// _ | / / / _ | (c) 2007-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: $ + +package scala.tools.partest + +import java.awt.event.{ActionEvent, ActionListener} +import java.io.{File, FilenameFilter, FileOutputStream, PrintStream} + +import scala.tools.nsc.Settings + +import utils.PrintMgr +import utils.PrintMgr._ + +/** + * @author Adriaan Moors, Thomas Hofer + * @version 1.0 + */ +class Test(val kind: String, val file: File) { + val dir = file.getParent + protected def baseSettings(settings: Settings) { + settings.classpath.value = dir + settings.outdir.value = { + var outDir = new File(dir, fileBase + "-" + kind + ".obj") + outDir.mkdir + outDir.toString + } + settings.deprecation.value = true + settings.nowarnings.value = false + settings.encoding.value = "iso-8859-1" + } + def defineSettings(settings: Settings) { + baseSettings(settings) + } + private def basename(name: String): String = { + val inx = name.lastIndexOf(".") + if (inx < 0) name else name.substring(0, inx) + } + val fileBase: String = basename(file.getName) + val logFile: File = new File(dir, fileBase + "-" + kind + ".log") + val checkFile: File = { + var chkFile = new File(dir, fileBase + ".check") + if (chkFile.isFile) { + chkFile + } else { + new File(dir, fileBase + "-" + kind + ".check") + } + } +} + +case class PosTest(override val file: File) extends Test("pos", file) +case class NegTest(override val file: File) extends Test("neg", file) +case class JVMTest(override val file: File) extends Test("jvm", file) { + override def defineSettings(settings: Settings) { + baseSettings(settings) + settings.target.value = + if (dir endsWith "jvm5") "jvm-1.5" else "jvm-1.4" + settings.classpath.value = System.getProperty("JVMEXTCP") + TestRunner.printVerbose("CLASSPATH="+settings.classpath.value +"\n") + } +} +case class ShootoutTest(override val file: File) extends Test("shootout", file) { + override def defineSettings(settings: Settings) { + baseSettings(settings) + settings.classpath.value = System.getProperty("JVMEXTCP") + } +} + +/** + * @author Stephane Micheloud + * @version 1.0 + */ +object TestRunner { + private var posCheck = false + private var negCheck = false + private var jvmCheck = false + private var runCheck = false + private var shootoutCheck = false + + private var conservative = false + private var verbose = false + + private var testDir: File = _ + private val con = new PrintStream(Console.out) + private var out = con + + private def go { + val master = new MasterActor(testDir, out) + val filter = new FilenameFilter { + def accept(dir: File, name: String): Boolean = name endsWith ".scala" + } + def getFiles(kind: String): List[File] = { + val kindDir = "files" + File.separator + kind + val dir = new File(testDir, kindDir) + if (dir.isDirectory) dir.listFiles(filter).toList + else { + println("Directory \"" + testDir.getPath + File.separator + kindDir + "\" not found") + Nil + } + } + + master.start + + if (posCheck) { + printOutline("Testing compiler (on files whose compilation should succeed)\n") + for (file <- getFiles("pos")) master ! PosTest(file) + } + if (negCheck) { + printOutline("Testing compiler (on files whose compilation should fail)\n") + for (file <- getFiles("neg")) master ! NegTest(file) + } + if (jvmCheck) { + printOutline("Testing JVM backend\n") + for (file <- getFiles("jvm")) master ! JVMTest(file) + for (file <- getFiles("run")) master ! JVMTest(file) + for (file <- getFiles("jvm5")) master ! JVMTest(file) + } else if (runCheck) { + printOutline("Testing JVM backend\n") + for (file <- getFiles("run")) master ! JVMTest(file) + } + if (shootoutCheck) { + printOutline("Testing shootout benchmarks\n") + for (file <- getFiles("shootout")) master! ShootoutTest(file) + } + + master ! ("start", conservative) + } + + private def printUsage { + println("Usage: TestRunner [<options>] <testdir> [<resfile>]") + println(" --pos ...") + println(" --neg ...") + println(" --jvm ...") + println(" --run ...") + println(" --shootout ...") + println(" --conservative ...") + println(" --verbose display progress information") + println + println("Send bugs to <scala@listes.epfl.ch>") + exit(1) + } + + final def printVerbose(msg: String) { + if (verbose) { + printOutline("debug : ") + println(msg) + } + } + + def main(args: Array[String]) { + if (args.length == 0) + printUsage + else { + for (arg <- args) { + arg match { + case "--pos" => posCheck = true + case "--neg" => negCheck = true + case "--jvm" => jvmCheck = true + case "--run" => runCheck = true + case "--shootout" => shootoutCheck = true + case "--conservative" => conservative = true + case "--verbose" => verbose = true + case _ => + if (testDir eq null) { + val dir = new File(arg) + if (dir.isDirectory) testDir = dir + else { + println("Directory \"" + arg + "\" not found") + exit(1) + } + } else if (out eq con) { + val file = new File(arg) + if (file.isFile || file.createNewFile) + out = new PrintStream(file) + else { + println("Result file \"" + arg + "\" not found") + exit(1) + } + } else + printUsage + } + } + if (!(posCheck | negCheck | jvmCheck | runCheck | shootoutCheck)) { + posCheck = true + negCheck = true + } + initialization(PrintMgr.MANY) + go + } + } +} diff --git a/src/partest/scala/tools/partest/WorkerActor.scala b/src/partest/scala/tools/partest/WorkerActor.scala new file mode 100644 index 0000000000..4b8bd3b4d0 --- /dev/null +++ b/src/partest/scala/tools/partest/WorkerActor.scala @@ -0,0 +1,307 @@ +/* __ *\ +** ________ ___ / / ___ Scala Parallel Testing ** +** / __/ __// _ | / / / _ | (c) 2007-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: $ + +package scala.tools.partest + +import java.io.{BufferedInputStream, BufferedReader, File, FileReader, + FileInputStream, FileOutputStream, InputStreamReader, + PrintStream, PrintWriter} +import java.net.URL + +import scala.actors.Actor +import scala.tools.nsc.{Global, ObjectRunner, Settings} +import scala.tools.nsc.reporters.{Reporter, AbstractReporter, ConsoleReporter} + + +class ExtConsoleReporter(override val settings: Settings, reader: BufferedReader, var writer: PrintWriter) extends ConsoleReporter(settings, reader, writer) { + def this(settings: Settings) = { + this(settings, Console.in, new PrintWriter("/dev/null")) + } + def hasWarnings: Boolean = WARNING.count != 0 +} + +class ExtGlobal(settings: Settings, reporter: Reporter) extends Global(settings, reporter) { + override def inform(msg: String) {} +} + +/** + * @author Adriaan Moors, Thomas Hofer + * @version 1.0 + */ +class WorkerActor(val master: MasterActor, val settings: Settings, var reporter: ExtConsoleReporter) extends Actor { + import scala.actors.Actor._ + + def newGlobal: ExtGlobal = new ExtGlobal(settings, reporter) + + def newGlobal(log: File): ExtGlobal = { + reporter = new ExtConsoleReporter(new Settings(x => ()), Console.in, new PrintWriter(log)) + reporter.shortname = true + newGlobal + } + + private def dirDelete(dir: File) { + if (dir.isDirectory) { + for (file <- dir.list) dirDelete(new File(dir, file)) + } + dir.delete + } + + def act() { + var compiler = newGlobal + val bufferSize = 1024 + val originBuffer = new Array[Byte](bufferSize) + val destBuffer = new Array[Byte](bufferSize) + loop { + react { + case (test: Test, bypass: Boolean, conservative: Boolean) => { + var bypassObjectRunner = bypass + if (!bypassObjectRunner) { + // TODO check the shootout source files for "dangerous" patterns, such as: Console.read, Scheduler.impl, + val dangerousCode = List("Console.read", "Scheduler.impl", "System.exit", "System.out").foldLeft("")((x, y) => x + " -e " + y) + val grepCmd = "grep " + test.file.getPath + " " + dangerousCode + TestRunner.printVerbose("grep cmd: " + grepCmd + "\n") + + val grep = Runtime.getRuntime.exec(grepCmd) + + val in = new BufferedReader(new InputStreamReader(grep.getInputStream)) + + val line = in.readLine + bypassObjectRunner = (line != null) + + in.close + + //println(bypassObjectRunner) + } + + var start = System.currentTimeMillis + //println("Starting..." + test.kind + " " + test.test) + + var toCompile = List(test.file.getPath) + + var outDir: File = new File(test.dir, test.fileBase + "-" + test.kind + ".obj") + if (! outDir.exists) { + outDir.mkdir + //println(this.toString + " " + "Created " + outDir) + } else { + //println(this.toString + " " + "Didn't need to create " + outDir) + } + test match { + case NegTest(_) => + compiler = newGlobal(test.logFile) + + case JVMTest(_) => + //println(test.file.getPath + ": " + test.checkFile.exists + " / " + test.logFile.exists) + if (test.checkFile.exists) { + var checkReader = new BufferedReader(new FileReader(test.checkFile)) + var firstLine = checkReader.readLine + if (firstLine != null && firstLine.startsWith("warning")) { + //reporter = new ExtConsoleReporter(new Settings(x => ()), Console.in, new PrintWriter(logFile)) + //reporter.shortname = true + compiler = newGlobal(test.logFile) + } + } else if (conservative) compiler = newGlobal + + case ShootoutTest(_) => + var testFile = new File(outDir, "Test.scala") + if (testFile.exists) { + toCompile = List(testFile.toString) + //println(this.toString + " ready to compile :" + toCompile) + } else { + // BASH script couldn't create test file... + } + if (test.checkFile.exists) { + var checkReader = new BufferedReader(new FileReader(test.checkFile)) + var firstLine = checkReader.readLine + if (firstLine.startsWith("warning")) { + //reporter = new ExtConsoleReporter(new Settings(x => ()), Console.in, new PrintWriter(logFile)) + //reporter.shortname = true + compiler = newGlobal(test.logFile) + } + } else if (conservative) compiler = newGlobal + + case _ => + } + + val c = compiler + + //println("about to define compilation settings...") + + test.defineSettings(settings) + try { + //println(this.toString + " " + "Launching compiler on " + toCompile) + (new c.Run) compile toCompile + reporter.printSummary + reporter.writer.flush + reporter.writer.close + //println(this.toString + " " + "Finished compiling " + test.fileBase) + } catch { + case e => { + e.printStackTrace + reporter.error(null, "IO/Error") + } + } + (reporter.hasErrors, test) match { + case (_, NegTest(_)) => + case (true, _) => + compiler = newGlobal + val c = compiler + try { + (new c.Run) compile toCompile + } catch { + case e => reporter.error(null, "IO/Error") + } + case _ => + } + + var result = test match { + case NegTest(_) => reporter.hasErrors + case _ => !reporter.hasErrors + } + + (bypassObjectRunner, result, test) match { + case (_, _, PosTest(_)) => + case (_, _, NegTest(_)) => + case (false, true, _) => + System.setProperty("scalatest.output", outDir.toString) + test match { + case ShootoutTest(_) => System.setProperty("scalatest.cwd", test.dir) + case _ => {} + } + + var classpath: List[URL] = outDir.toURL :: List((new File(test.dir)).toURL) ::: List.fromString(System.getProperty("CLASSPATH"), ':').map(x => (new File(x)).toURL) ::: List.fromString(System.getProperty("JVMEXTCP"), ':').map(x => (new File(x)).toURL) + + try { + //println(this.toString + " " + "Launching test " + test.fileBase) + var out = new FileOutputStream(test.logFile, true) + Console.withOut(new PrintStream(out)) { + ObjectRunner.run(classpath, "Test", List("jvm")) + } + out.flush + out.close + //println(this.toString + " " + "Finished running " + test.fileBase) + } catch { case t => println(t) } + + case _ => + } + (!bypassObjectRunner && result, test.checkFile.exists, test) match { + case (_, _, PosTest(_)) => + case (true, true, _) => + /*var cmd: String = "diff " + test.logFile + " " + test.checkFile + //println(this.toString + " Comparing files " + test.fileBase) + var proc: Process = Runtime.getRuntime.exec(cmd) + proc.waitFor + result = (proc.exitValue == 0)*/ + var equalNow = true + if (test.checkFile.canRead) { + val originStream = new FileInputStream(test.logFile) + val destStream = new FileInputStream(test.checkFile) + var originSize = originStream.read(originBuffer) + while (originSize >= 0) { + if (originSize == destStream.read(destBuffer)) { + for (idx <- 0 until originSize) + equalNow = equalNow && (originBuffer(idx) == destBuffer(idx)) + if (!equalNow) { + result = false + //println("Diff1: diffs found") + } + } + else { + result = false + //println("Diff1: diffs found") + } + originSize = originStream.read(originBuffer) + } + if (destStream.read(destBuffer) >= 0) result = false + } + + case _ => + //println("Not testing diff... " + test.test) + } + + (bypassObjectRunner || !result, test) match { + case (_, PosTest(_)) => + case (_, NegTest(_)) => + case (true, _) => + result = true + var javaoptsFile = new File(test.dir, test.fileBase + ".javaopts") + //var javaNewOpts = (new BufferedFileReader(javaoptsFile)).readLine + //if (javaoptsFile.exists && javaNewOpts != null) {} + //Use Runtime.exec to execute the compiled file and pipe the standard system out and the console out to the logfile + var cmd = "env JAVACMD=java JAVA_OPTS=-Djava.library.path="+test.dir+" "+System.getProperty("SCALA")+" -Dscalatest.lib="+System.getProperty("scalatest.lib")+" -Dscalatest.cwd="+test.dir+" -Dscalatest.output="+outDir+" -classpath "+outDir+":"+System.getProperty("CLASSPATH")+":"+System.getProperty("JVMEXTCP")+" Test jvm" + + //println(cmd) + + var execution = Runtime.getRuntime.exec(cmd) + + var in = execution.getInputStream + var out = new FileOutputStream(test.logFile) + + var c = in.read + while (c != -1) { + out.write(c) + c = in.read + } + + in.close + out.close + + //println("Running diff") + + /*var diff = Runtime.getRuntime.exec("diff " + test.logFile + " " + test.checkFile) + diff.waitFor + + result = (diff.exitValue == 0)*/ + var equalNow = true + if (test.checkFile.canRead) { + val originStream = new FileInputStream(test.logFile) + val destStream = new FileInputStream(test.checkFile) + var originSize = originStream.read(originBuffer) + while (originSize >= 0) { + if (originSize == destStream.read(destBuffer)) { + for (idx <- 0 until originSize) + equalNow = equalNow && (originBuffer(idx) == destBuffer(idx)) + if (!equalNow) { + result = false + //println("Differences found between the log and check files..") + } + } + else { + result = false + //println("Differences found between the log and check files..") + } + + originSize = originStream.read(originBuffer) + } + if (destStream.read(destBuffer) >= 0) result = false + } + //else reportMissing(originFile) + + case _ => + //println("Not Using runtime... " + test.test) + } + test.logFile.delete + + var end = System.currentTimeMillis + + //println(test.test + ": " + (end - start)) + + //printSuccess(this.toString + " " + fileBase + ": "+ result + "\n") + master ! (test.kind, result, test.file) + dirDelete(outDir) + } + case false => + exit + + case msg => + println("Unknown message : " + msg) + } + } + } +} diff --git a/src/partest/scala/tools/partest/utils/PrintMgr.scala b/src/partest/scala/tools/partest/utils/PrintMgr.scala new file mode 100644 index 0000000000..a47657d611 --- /dev/null +++ b/src/partest/scala/tools/partest/utils/PrintMgr.scala @@ -0,0 +1,51 @@ +/* __ *\ +** ________ ___ / / ___ Scala Parallel Testing ** +** / __/ __// _ | / / / _ | (c) 2007-2008, LAMP/EPFL ** +** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** +** /____/\___/_/ |_/____/_/ | | ** +** |/ ** +\* */ + +// $Id: $ + +package scala.tools.partest.utils + +/** + * @author Thomas Hofer + */ +object PrintMgr { + + val NONE = 0 + val SOME = 1 + val MANY = 2 + + var outline = "" + var success = "" + var failure = "" + var warning = "" + var default = "" + + def initialization(number: Int) = number match { + case MANY => + outline = Console.BOLD + Console.BLACK + success = Console.BOLD + Console.GREEN + failure = Console.BOLD + Console.RED + warning = Console.BOLD + Console.YELLOW + default = Console.RESET + case SOME => + outline = Console.BOLD + Console.BLACK + success = Console.RESET + failure = Console.BOLD + Console.BLACK + warning = Console.BOLD + Console.BLACK + default = Console.RESET + case _ => + } + + def printOutline(msg: String) = print(outline + msg + default) + + def printSuccess(msg: String) = print(success + msg + default) + + def printFailure(msg: String) = print(failure + msg + default) + + def printWarning(msg: String) = print(warning + msg + default) +} |