Skip to content

Commit

Permalink
Add infrastructure to run the JUnit tests of upstream Scala.js.
Browse files Browse the repository at this point in the history
And run one test for now: `compiler/IntTest.scala`.
  • Loading branch information
sjrd committed May 21, 2019
1 parent dc619e5 commit 282caf6
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 24 deletions.
2 changes: 1 addition & 1 deletion .drone.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pipeline:
image: lampepfl/dotty:2019-04-22
commands:
- cp -R . /tmp/2/ && cd /tmp/2/
- ./project/scripts/sbt ";dotty-bootstrapped/compile ;dotty-bootstrapped/test; dotty-semanticdb/compile; dotty-semanticdb/test:compile;sjsSandbox/run;sjsSandbox/test"
- ./project/scripts/sbt ";dotty-bootstrapped/compile ;dotty-bootstrapped/test; dotty-semanticdb/compile; dotty-semanticdb/test:compile;sjsSandbox/run;sjsSandbox/test;sjsJUnitTests/test"
- ./project/scripts/bootstrapCmdTests

community_build:
Expand Down
2 changes: 2 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ val `dotty-compiler` = Build.`dotty-compiler`
val `dotty-compiler-bootstrapped` = Build.`dotty-compiler-bootstrapped`
val `dotty-library` = Build.`dotty-library`
val `dotty-library-bootstrapped` = Build.`dotty-library-bootstrapped`
val `dotty-library-bootstrappedJS` = Build.`dotty-library-bootstrappedJS`
val `dotty-sbt-bridge` = Build.`dotty-sbt-bridge`
val `dotty-sbt-bridge-tests` = Build.`dotty-sbt-bridge-tests`
val `dotty-language-server` = Build.`dotty-language-server`
Expand All @@ -23,6 +24,7 @@ val `dist-bootstrapped` = Build.`dist-bootstrapped`
val `community-build` = Build.`community-build`

val sjsSandbox = Build.sjsSandbox
val sjsJUnitTests = Build.sjsJUnitTests

val `sbt-dotty` = Build.`sbt-dotty`
val `vscode-dotty` = Build.`vscode-dotty`
Expand Down
160 changes: 137 additions & 23 deletions project/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,35 @@ import sbtbuildinfo.BuildInfoPlugin.autoImport._

import scala.util.Properties.isJavaAtLeast

object MyScalaJSPlugin extends AutoPlugin {
import Build._

override def requires: Plugins = ScalaJSPlugin

override def projectSettings: Seq[Setting[_]] = Def.settings(
commonBootstrappedSettings,

/* Remove the Scala.js compiler plugin for scalac, and enable the
* Scala.js back-end of dotty instead.
*/
libraryDependencies := {
val deps = libraryDependencies.value
deps.filterNot(_.name.startsWith("scalajs-compiler")).map(_.withDottyCompat(scalaVersion.value))
},
scalacOptions += "-scalajs",

// Replace the JVM JUnit dependency by the Scala.js one
libraryDependencies ~= {
_.filter(!_.name.startsWith("junit-interface"))
},
libraryDependencies +=
("org.scala-js" %% "scalajs-junit-test-runtime" % scalaJSVersion % "test").withDottyCompat(scalaVersion.value),

// Typecheck the Scala.js IR found on the classpath
scalaJSLinkerConfig ~= (_.withCheckIR(true)),
)
}

object Build {
val scalacVersion = "2.12.8"
val referenceVersion = "0.14.0-RC1"
Expand Down Expand Up @@ -101,6 +130,8 @@ object Build {
val ideTestsCompilerArguments = taskKey[Seq[String]]("Compiler arguments to use in IDE tests")
val ideTestsDependencyClasspath = taskKey[Seq[File]]("Dependency classpath to use in IDE tests")

val fetchScalaJSSource = taskKey[File]("Fetch the sources of Scala.js")

lazy val SourceDeps = config("sourcedeps")

// Settings shared by the build (scoped in ThisBuild). Used in build.sbt
Expand Down Expand Up @@ -712,6 +743,23 @@ object Build {
case Bootstrapped => `dotty-library-bootstrapped`
}

/** The dotty standard library compiled with the Scala.js back-end, to produce
* the corresponding .sjsir files.
*
* This artifact must be on the classpath on every "Dotty.js" project.
*
* Currently, only a very small fraction of the dotty library is actually
* included in this project, and hence available to Dotty.js projects. More
* will be added in the future as things are confirmed to be supported.
*/
lazy val `dotty-library-bootstrappedJS`: Project = project.in(file("library-js")).
asDottyLibrary(Bootstrapped).
enablePlugins(MyScalaJSPlugin).
settings(
unmanagedSourceDirectories in Compile :=
(unmanagedSourceDirectories in (`dotty-library-bootstrapped`, Compile)).value,
)

lazy val `dotty-sbt-bridge` = project.in(file("sbt-bridge/src")).
// We cannot depend on any bootstrapped project to compile the bridge, since the
// bridge is needed to compile these projects.
Expand Down Expand Up @@ -817,36 +865,102 @@ object Build {
* useful, as that would not provide the linker and JS runners.
*/
lazy val sjsSandbox = project.in(file("sandbox/scalajs")).
enablePlugins(ScalaJSPlugin).
dependsOn(dottyLibrary(Bootstrapped)).
settings(commonBootstrappedSettings).
enablePlugins(MyScalaJSPlugin).
dependsOn(`dotty-library-bootstrappedJS`).
settings(
/* Remove the Scala.js compiler plugin for scalac, and enable the
* Scala.js back-end of dotty instead.
*/
libraryDependencies := {
val deps = libraryDependencies.value
deps.filterNot(_.name.startsWith("scalajs-compiler")).map(_.withDottyCompat(scalaVersion.value))
},
scalacOptions += "-scalajs",
scalaJSUseMainModuleInitializer := true,
)

/** Scala.js test suite.
*
* This project downloads the sources of the upstream Scala.js test suite,
* and tests them with the dotty Scala.js back-end. Currently, only a very
* small fraction of the upstream test suite is actually compiled and run.
* It will grow in the future, as more stuff is confirmed to be supported.
*/
lazy val sjsJUnitTests = project.in(file("tests/sjs-junit")).
enablePlugins(MyScalaJSPlugin).
dependsOn(`dotty-library-bootstrappedJS`).
settings(
scalacOptions --= Seq("-Xfatal-warnings", "-deprecation"),

sourceDirectory in fetchScalaJSSource := target.value / s"scala-js-src-$scalaJSVersion",

fetchScalaJSSource := {
import org.eclipse.jgit.api._

val s = streams.value
val ver = scalaJSVersion
val trgDir = (sourceDirectory in fetchScalaJSSource).value

if (!trgDir.exists) {
s.log.info(s"Fetching Scala.js source version $ver")
IO.createDirectory(trgDir)
new CloneCommand()
.setDirectory(trgDir)
.setURI("https://github.com/scala-js/scala-js.git")
.call()
}

// Checkout proper ref. We do this anyway so we fail if something is wrong
val git = Git.open(trgDir)
s.log.info(s"Checking out Scala.js source version $ver")
git.checkout().setName(s"v$ver").call()

// Replace the JVM JUnit dependency by the Scala.js one
libraryDependencies ~= {
_.filter(!_.name.startsWith("junit-interface"))
trgDir
},

// We need JUnit in the Compile configuration
libraryDependencies +=
("org.scala-js" %% "scalajs-junit-test-runtime" % scalaJSVersion % "test").withDottyCompat(scalaVersion.value),
("org.scala-js" %% "scalajs-junit-test-runtime" % scalaJSVersion).withDottyCompat(scalaVersion.value),

// The main class cannot be found automatically due to the empty inc.Analysis
mainClass in Compile := Some("hello.HelloWorld"),
sourceGenerators in Compile += Def.task {
import org.scalajs.linker.CheckedBehavior

scalaJSUseMainModuleInitializer := true,
val stage = scalaJSStage.value

/* Debug-friendly Scala.js optimizer options.
* In particular, typecheck the Scala.js IR found on the classpath.
*/
scalaJSLinkerConfig ~= {
_.withCheckIR(true).withParallel(false)
val linkerConfig = stage match {
case FastOptStage => (scalaJSLinkerConfig in (Compile, fastOptJS)).value
case FullOptStage => (scalaJSLinkerConfig in (Compile, fullOptJS)).value
}

val moduleKind = linkerConfig.moduleKind
val sems = linkerConfig.semantics

ConstantHolderGenerator.generate(
(sourceManaged in Compile).value,
"org.scalajs.testsuite.utils.BuildInfo",
"scalaVersion" -> scalaVersion.value,
"hasSourceMaps" -> false, //MyScalaJSPlugin.wantSourceMaps.value,
"isNoModule" -> (moduleKind == ModuleKind.NoModule),
"isESModule" -> (moduleKind == ModuleKind.ESModule),
"isCommonJSModule" -> (moduleKind == ModuleKind.CommonJSModule),
"isFullOpt" -> (stage == FullOptStage),
"compliantAsInstanceOfs" -> (sems.asInstanceOfs == CheckedBehavior.Compliant),
"compliantArrayIndexOutOfBounds" -> (sems.arrayIndexOutOfBounds == CheckedBehavior.Compliant),
"compliantModuleInit" -> (sems.moduleInit == CheckedBehavior.Compliant),
"strictFloats" -> sems.strictFloats,
"productionMode" -> sems.productionMode,
"es2015" -> linkerConfig.esFeatures.useECMAScript2015,
)
}.taskValue,

managedSources in Compile ++= {
val dir = fetchScalaJSSource.value / "test-suite/js/src/main/scala"
val filter = (
("*.scala": FileFilter)
-- "Typechecking*.scala"
-- "NonNativeTypeTestSeparateRun.scala"
)
(dir ** filter).get
},

managedSources in Test ++= {
val dir = fetchScalaJSSource.value / "test-suite"
(
(dir / "shared/src/test/scala/org/scalajs/testsuite/compiler" ** "IntTest.scala").get
++ (dir / "shared/src/test/scala/org/scalajs/testsuite/utils" ** "*.scala").get
)
}
)

Expand Down
44 changes: 44 additions & 0 deletions project/ConstantHolderGenerator.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import scala.annotation.tailrec

import sbt._

import org.scalajs.ir.ScalaJSVersions

object ConstantHolderGenerator {
/** Generate a *.scala file that contains the given values as literals. */
def generate(dir: File, fqn: String, values: (String, Any)*): Seq[File] = {
val (fullPkg@(_ :+ pkg)) :+ objectName = fqn.split('.').toSeq

val out = dir / (objectName + ".scala")

val defs = for {
(name, value) <- values
} yield {
s"val $name = ${literal(value)}"
}

val scalaCode =
s"""
package ${fullPkg.mkString(".")}

private[$pkg] object $objectName {
${defs.mkString("\n")}
}
"""

IO.write(out, scalaCode)

Seq(out)
}

@tailrec
private final def literal(v: Any): String = v match {
case s: String => "raw\"\"\"" + s + "\"\"\""
case b: Boolean => b.toString
case f: File => literal(f.getAbsolutePath)

case _ =>
throw new IllegalArgumentException(
"Unsupported value type: " + v.getClass)
}
}

0 comments on commit 282caf6

Please sign in to comment.