summaryrefslogtreecommitdiff
path: root/examples/scala-js/partest/src/main/scala/scala/tools/partest/scalajs
diff options
context:
space:
mode:
Diffstat (limited to 'examples/scala-js/partest/src/main/scala/scala/tools/partest/scalajs')
-rw-r--r--examples/scala-js/partest/src/main/scala/scala/tools/partest/scalajs/PartestInterface.scala119
-rw-r--r--examples/scala-js/partest/src/main/scala/scala/tools/partest/scalajs/ScalaJSPartest.scala203
-rw-r--r--examples/scala-js/partest/src/main/scala/scala/tools/partest/scalajs/ScalaJSPartestOptions.scala109
3 files changed, 431 insertions, 0 deletions
diff --git a/examples/scala-js/partest/src/main/scala/scala/tools/partest/scalajs/PartestInterface.scala b/examples/scala-js/partest/src/main/scala/scala/tools/partest/scalajs/PartestInterface.scala
new file mode 100644
index 0000000..0dc2189
--- /dev/null
+++ b/examples/scala-js/partest/src/main/scala/scala/tools/partest/scalajs/PartestInterface.scala
@@ -0,0 +1,119 @@
+/* NOTE
+ * Most of this file is copy-pasted from
+ * https://github.com/scala/scala-partest-interface
+ * It is unfortunately not configurable enough, hence the duplication
+ */
+
+package scala.tools.partest
+package scalajs
+
+import scala.language.reflectiveCalls
+
+import sbt.testing.Fingerprint
+import sbt.testing.TaskDef
+import sbt.testing.EventHandler
+import sbt.testing.Logger
+import sbt.testing.Task
+import sbt.testing.AnnotatedFingerprint
+import java.net.URLClassLoader
+import java.io.File
+
+object Framework {
+ // as partest is not driven by test classes discovered by sbt, need to add this marker fingerprint to definedTests
+ val fingerprint = new AnnotatedFingerprint { def isModule = true; def annotationName = "partest" }
+
+ // TODO how can we export `fingerprint` so that a user can just add this to their build.sbt
+ // definedTests in Test += new sbt.TestDefinition("partest", fingerprint, true, Array())
+}
+class Framework extends sbt.testing.Framework {
+ def fingerprints: Array[Fingerprint] = Array(Framework.fingerprint)
+ def name: String = "partest"
+
+ def runner(args: Array[String], remoteArgs: Array[String], testClassLoader: ClassLoader): sbt.testing.Runner =
+ new Runner(args, remoteArgs, testClassLoader)
+}
+
+/** Represents one run of a suite of tests.
+ */
+case class Runner(args: Array[String], remoteArgs: Array[String], testClassLoader: ClassLoader) extends sbt.testing.Runner {
+ /** Returns an array of tasks that when executed will run tests and suites determined by the
+ * passed <code>TaskDef</code>s.
+ *
+ * <p>
+ * Each returned task, when executed, will run tests and suites determined by the
+ * test class name, fingerprints, "explicitly specified" field, and selectors of one of the passed <code>TaskDef</code>s.
+ * </p>
+ *
+ * <p>
+ * This <code>tasks</code> method may be called with <code>TaskDef</code>s containing the same value for <code>testClassName</code> but
+ * different fingerprints. For example, if both a class and its companion object were test classes, the <code>tasks</code> method could be
+ * passed an array containing <code>TaskDef</code>s with the same name but with a different value for <code>fingerprint.isModule</code>.
+ * </p>
+ *
+ * <p>
+ * A test framework may "reject" a requested task by returning no <code>Task</code> for that <code>TaskDef</code>.
+ * </p>
+ *
+ * @param taskDefs the <code>TaskDef</code>s for requested tasks
+ * @return an array of <code>Task</code>s
+ * @throws IllegalStateException if invoked after <code>done</code> has been invoked.
+ */
+ def tasks(taskDefs: Array[TaskDef]): Array[sbt.testing.Task] =
+ taskDefs map (PartestTask(_, args): sbt.testing.Task)
+
+ /** Indicates the client is done with this <code>Runner</code> instance.
+ *
+ * @return a possibly multi-line summary string, or the empty string if no summary is provided -- TODO
+ */
+ def done(): String = ""
+}
+
+/** Run partest in this VM. Assumes we're running in a forked VM!
+ *
+ * TODO: make configurable
+ */
+case class PartestTask(taskDef: TaskDef, args: Array[String]) extends Task {
+
+ // Get scala version through test name
+ val scalaVersion = taskDef.fullyQualifiedName.stripPrefix("partest-")
+
+ /** Executes this task, possibly returning to the client new tasks to execute. */
+ def execute(eventHandler: EventHandler, loggers: Array[Logger]): Array[Task] = {
+ val forkedCp = scala.util.Properties.javaClassPath
+ val classLoader = new URLClassLoader(forkedCp.split(java.io.File.pathSeparator).map(new File(_).toURI.toURL))
+
+ if (Runtime.getRuntime().maxMemory() / (1024*1024) < 800)
+ loggers foreach (_.warn(s"""Low heap size detected (~ ${Runtime.getRuntime().maxMemory() / (1024*1024)}M). Please add the following to your build.sbt: javaOptions in Test += "-Xmx1G""""))
+
+ val maybeOptions =
+ ScalaJSPartestOptions(args, str => loggers.foreach(_.error(str)))
+
+ maybeOptions foreach { options =>
+ val runner = SBTRunner(
+ Framework.fingerprint, eventHandler, loggers,
+ new File(s"../partest/fetchedSources/${scalaVersion}"),
+ classLoader, null, null, Array.empty[String], options, scalaVersion)
+
+ try runner execute Array("run", "pos", "neg")
+ catch {
+ case ex: ClassNotFoundException =>
+ loggers foreach { l => l.error("Please make sure partest is running in a forked VM by including the following line in build.sbt:\nfork in Test := true") }
+ throw ex
+ }
+ }
+
+ Array()
+ }
+
+ type SBTRunner = { def execute(kinds: Array[String]): String }
+
+ // use reflection to instantiate scala.tools.partest.scalajs.ScalaJSSBTRunner,
+ // casting to the structural type SBTRunner above so that method calls on the result will be invoked reflectively as well
+ private def SBTRunner(partestFingerprint: Fingerprint, eventHandler: EventHandler, loggers: Array[Logger], testRoot: File, testClassLoader: URLClassLoader, javaCmd: File, javacCmd: File, scalacArgs: Array[String], options: ScalaJSPartestOptions, scalaVersion: String): SBTRunner = {
+ val runnerClass = Class.forName("scala.tools.partest.scalajs.ScalaJSSBTRunner")
+ runnerClass.getConstructors()(0).newInstance(partestFingerprint, eventHandler, loggers, testRoot, testClassLoader, javaCmd, javacCmd, scalacArgs, options, scalaVersion).asInstanceOf[SBTRunner]
+ }
+
+ /** A possibly zero-length array of string tags associated with this task. */
+ def tags: Array[String] = Array()
+}
diff --git a/examples/scala-js/partest/src/main/scala/scala/tools/partest/scalajs/ScalaJSPartest.scala b/examples/scala-js/partest/src/main/scala/scala/tools/partest/scalajs/ScalaJSPartest.scala
new file mode 100644
index 0000000..edd0ea9
--- /dev/null
+++ b/examples/scala-js/partest/src/main/scala/scala/tools/partest/scalajs/ScalaJSPartest.scala
@@ -0,0 +1,203 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2013 LAMP/EPFL
+ * @author Sébastien Doeraene
+ */
+
+package scala.tools.partest
+package scalajs
+
+import nest._
+import Path._
+
+import scala.tools.nsc.{ Global, Settings }
+import scala.tools.nsc.reporters.{ Reporter }
+import scala.tools.nsc.plugins.Plugin
+
+import scala.scalajs.compiler.ScalaJSPlugin
+
+import scala.io.Source
+
+import sbt.testing.{ EventHandler, Logger, Fingerprint }
+import java.io.File
+import java.net.URLClassLoader
+
+trait ScalaJSDirectCompiler extends DirectCompiler {
+ override def newGlobal(settings: Settings, reporter: Reporter): PartestGlobal = {
+ new PartestGlobal(settings, reporter) {
+ override protected def loadRoughPluginsList(): List[Plugin] = {
+ (super.loadRoughPluginsList() :+
+ Plugin.instantiate(classOf[ScalaJSPlugin], this))
+ }
+ }
+ }
+}
+
+class ScalaJSRunner(testFile: File, suiteRunner: SuiteRunner,
+ scalaJSOverridePath: String, options: ScalaJSPartestOptions,
+ noWarnFile: File) extends nest.Runner(testFile, suiteRunner) {
+
+ private val compliantSems: List[String] = {
+ scalaJSConfigFile("sem").fold(List.empty[String]) { file =>
+ Source.fromFile(file).getLines.toList
+ }
+ }
+
+ override val checkFile: File = {
+ scalaJSConfigFile("check") getOrElse {
+ // this is super.checkFile, but apparently we can't do that
+ new FileOps(testFile).changeExtension("check")
+ }
+ }
+
+ private def scalaJSConfigFile(ext: String): Option[File] = {
+ val overrideFile = s"$scalaJSOverridePath/$kind/$fileBase.$ext"
+ val url = getClass.getResource(overrideFile)
+ Option(url).map(url => new File(url.toURI))
+ }
+
+ override def newCompiler = new DirectCompiler(this) with ScalaJSDirectCompiler
+ override def extraJavaOptions = {
+ super.extraJavaOptions ++ Seq(
+ s"-Dscalajs.partest.noWarnFile=${noWarnFile.getAbsolutePath}",
+ s"-Dscalajs.partest.optMode=${options.optMode.id}",
+ s"-Dscalajs.partest.compliantSems=${compliantSems.mkString(",")}"
+ )
+ }
+}
+
+trait ScalaJSSuiteRunner extends SuiteRunner {
+
+ // Stuff to mix in
+
+ val options: ScalaJSPartestOptions
+
+ /** Full scala version name. Used to discover blacklist (etc.) files */
+ val scalaVersion: String
+
+ // Stuff we provide
+
+ override def banner: String = {
+ import scala.scalajs.ir.ScalaJSVersions.{ current => currentVersion }
+
+ super.banner.trim + s"""
+ |Scala.js version is: $currentVersion
+ |Scala.js options are:
+ |optimizer: ${options.optMode.shortStr}
+ |testFilter: ${options.testFilter.descr}
+ """.stripMargin
+ }
+
+ override def runTest(testFile: File): TestState = {
+ // Mostly copy-pasted from SuiteRunner.runTest(), unfortunately :-(
+ val runner = new ScalaJSRunner(testFile, this, listDir, options, noWarnFile)
+
+ // when option "--failed" is provided execute test only if log
+ // is present (which means it failed before)
+ val state =
+ if (failed && !runner.logFile.canRead)
+ runner.genPass()
+ else {
+ val (state, elapsed) =
+ try timed(runner.run())
+ catch {
+ case t: Throwable => throw new RuntimeException(s"Error running $testFile", t)
+ }
+ NestUI.reportTest(state)
+ runner.cleanup()
+ state
+ }
+ onFinishTest(testFile, state)
+ }
+
+ override def runTestsForFiles(kindFiles: Array[File],
+ kind: String): Array[TestState] = {
+ super.runTestsForFiles(kindFiles.filter(shouldUseTest), kind)
+ }
+
+ private lazy val listDir =
+ s"/scala/tools/partest/scalajs/$scalaVersion"
+
+ private lazy val buglistedTestFileNames =
+ readTestList(s"$listDir/BuglistedTests.txt")
+
+ private lazy val blacklistedTestFileNames =
+ readTestList(s"$listDir/BlacklistedTests.txt")
+
+ private lazy val whitelistedTestFileNames =
+ readTestList(s"$listDir/WhitelistedTests.txt")
+
+ private lazy val noWarnFile: File = {
+ val url = getClass.getResource(s"$listDir/NoDCEWarn.txt")
+ assert(url != null, "Need NoDCEWarn.txt file")
+ new File(url.toURI).getAbsolutePath()
+ }
+
+ private def readTestList(resourceName: String): Set[String] = {
+ val source = scala.io.Source.fromURL(getClass.getResource(resourceName))
+
+ val fileNames = for {
+ line <- source.getLines
+ trimmed = line.trim
+ if trimmed != "" && !trimmed.startsWith("#")
+ } yield extendShortTestName(trimmed)
+
+ fileNames.toSet
+ }
+
+ private def extendShortTestName(testName: String) = {
+ val srcDir = PathSettings.srcDir
+ (srcDir / testName).toCanonical.getAbsolutePath
+ }
+
+ private lazy val testFilter: String => Boolean = {
+ import ScalaJSPartestOptions._
+ options.testFilter match {
+ case UnknownTests => { absPath =>
+ !blacklistedTestFileNames.contains(absPath) &&
+ !whitelistedTestFileNames.contains(absPath) &&
+ !buglistedTestFileNames.contains(absPath)
+ }
+ case BlacklistedTests => blacklistedTestFileNames
+ case BuglistedTests => buglistedTestFileNames
+ case WhitelistedTests => whitelistedTestFileNames
+ case SomeTests(names) => names.map(extendShortTestName _).toSet
+ }
+ }
+
+ private def shouldUseTest(testFile: File): Boolean = {
+ val absPath = testFile.toCanonical.getAbsolutePath
+ testFilter(absPath)
+ }
+}
+
+/* Pre-mixin ScalaJSSuiteRunner in SBTRunner, because this is looked up
+ * via reflection from the sbt partest interface of Scala.js
+ */
+class ScalaJSSBTRunner(
+ partestFingerprint: Fingerprint,
+ eventHandler: EventHandler,
+ loggers: Array[Logger],
+ testRoot: File,
+ testClassLoader: URLClassLoader,
+ javaCmd: File,
+ javacCmd: File,
+ scalacArgs: Array[String],
+ val options: ScalaJSPartestOptions,
+ val scalaVersion: String
+) extends SBTRunner(
+ partestFingerprint, eventHandler, loggers, "test/files", testClassLoader,
+ javaCmd, javacCmd, scalacArgs
+) with ScalaJSSuiteRunner {
+
+ // The test root for partest is read out through the system properties,
+ // not passed as an argument
+ sys.props("partest.root") = testRoot.getAbsolutePath()
+
+ // Partests take at least 5h. We double, just to be sure. (default is 4 hours)
+ sys.props("partest.timeout") = "10 hours"
+
+ // Set showDiff on global UI module
+ if (options.showDiff)
+ NestUI.setDiffOnFail()
+
+}
diff --git a/examples/scala-js/partest/src/main/scala/scala/tools/partest/scalajs/ScalaJSPartestOptions.scala b/examples/scala-js/partest/src/main/scala/scala/tools/partest/scalajs/ScalaJSPartestOptions.scala
new file mode 100644
index 0000000..1f1680a
--- /dev/null
+++ b/examples/scala-js/partest/src/main/scala/scala/tools/partest/scalajs/ScalaJSPartestOptions.scala
@@ -0,0 +1,109 @@
+package scala.tools.partest.scalajs
+
+class ScalaJSPartestOptions private (
+ val testFilter: ScalaJSPartestOptions.TestFilter,
+ val optMode: ScalaJSPartestOptions.OptMode,
+ val showDiff: Boolean
+)
+
+object ScalaJSPartestOptions {
+
+ sealed abstract class TestFilter {
+ def descr: String
+ }
+ case object UnknownTests extends TestFilter {
+ override def descr: String = "Unknown"
+ }
+ case object BlacklistedTests extends TestFilter {
+ override def descr: String = "Blacklisted"
+ }
+ case object WhitelistedTests extends TestFilter {
+ override def descr: String = "Whitelisted"
+ }
+ case object BuglistedTests extends TestFilter {
+ override def descr: String = "Buglisted"
+ }
+ case class SomeTests(names: List[String]) extends TestFilter {
+ override def descr: String = "Custom " + this.toString
+ override def toString() =
+ names.map(x => s""""$x"""").mkString("[", ", ", "]")
+ }
+
+ sealed abstract class OptMode {
+ def shortStr: String
+ def id: String
+ }
+ object OptMode {
+ def fromId(id: String): OptMode = id match {
+ case "none" => NoOpt
+ case "fast" => FastOpt
+ case "full" => FullOpt
+ case _ => sys.error(s"Unknown optimization mode: $id")
+ }
+ }
+ case object NoOpt extends OptMode {
+ def shortStr: String = "None"
+ def id: String = "none"
+ }
+ case object FastOpt extends OptMode {
+ def shortStr: String = "Fast"
+ def id: String = "fast"
+ }
+ case object FullOpt extends OptMode {
+ def shortStr: String = "Full"
+ def id: String = "full"
+ }
+
+ def apply(args: Array[String],
+ errorReporter: String => Unit): Option[ScalaJSPartestOptions] = {
+
+ var failed = false
+
+ var filter: Option[TestFilter] = None
+ var optMode: OptMode = NoOpt
+ var showDiff: Boolean = false
+
+ def error(msg: String) = {
+ failed = true
+ errorReporter(msg)
+ }
+
+ def setFilter(newFilter: TestFilter) = (filter, newFilter) match {
+ case (Some(SomeTests(oldNames)), SomeTests(newNames)) =>
+ // Merge test names
+ filter = Some(SomeTests(oldNames ++ newNames))
+ case (Some(fil), newFilter) =>
+ error(s"You cannot specify twice what tests to use (already specified: $fil, new: $newFilter)")
+ case (None, newFilter) =>
+ filter = Some(newFilter)
+ }
+
+ for (arg <- args) arg match {
+ case "--fastOpt" =>
+ optMode = FastOpt
+ case "--noOpt" =>
+ optMode = NoOpt
+ case "--fullOpt" =>
+ optMode = FullOpt
+ case "--blacklisted" =>
+ setFilter(BlacklistedTests)
+ case "--buglisted" =>
+ setFilter(BuglistedTests)
+ case "--whitelisted" =>
+ setFilter(WhitelistedTests)
+ case "--unknown" =>
+ setFilter(UnknownTests)
+ case "--showDiff" =>
+ showDiff = true
+ case _ =>
+ setFilter(SomeTests(arg :: Nil))
+ }
+
+ if (failed) None
+ else Some {
+ new ScalaJSPartestOptions(
+ filter.getOrElse(WhitelistedTests), optMode, showDiff)
+ }
+ }
+
+}