Skip to content

Commit 282caf6

Browse files
committed
Add infrastructure to run the JUnit tests of upstream Scala.js.
And run one test for now: `compiler/IntTest.scala`.
1 parent dc619e5 commit 282caf6

File tree

4 files changed

+184
-24
lines changed

4 files changed

+184
-24
lines changed

.drone.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ pipeline:
3131
image: lampepfl/dotty:2019-04-22
3232
commands:
3333
- cp -R . /tmp/2/ && cd /tmp/2/
34-
- ./project/scripts/sbt ";dotty-bootstrapped/compile ;dotty-bootstrapped/test; dotty-semanticdb/compile; dotty-semanticdb/test:compile;sjsSandbox/run;sjsSandbox/test"
34+
- ./project/scripts/sbt ";dotty-bootstrapped/compile ;dotty-bootstrapped/test; dotty-semanticdb/compile; dotty-semanticdb/test:compile;sjsSandbox/run;sjsSandbox/test;sjsJUnitTests/test"
3535
- ./project/scripts/bootstrapCmdTests
3636

3737
community_build:

build.sbt

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ val `dotty-compiler` = Build.`dotty-compiler`
77
val `dotty-compiler-bootstrapped` = Build.`dotty-compiler-bootstrapped`
88
val `dotty-library` = Build.`dotty-library`
99
val `dotty-library-bootstrapped` = Build.`dotty-library-bootstrapped`
10+
val `dotty-library-bootstrappedJS` = Build.`dotty-library-bootstrappedJS`
1011
val `dotty-sbt-bridge` = Build.`dotty-sbt-bridge`
1112
val `dotty-sbt-bridge-tests` = Build.`dotty-sbt-bridge-tests`
1213
val `dotty-language-server` = Build.`dotty-language-server`
@@ -23,6 +24,7 @@ val `dist-bootstrapped` = Build.`dist-bootstrapped`
2324
val `community-build` = Build.`community-build`
2425

2526
val sjsSandbox = Build.sjsSandbox
27+
val sjsJUnitTests = Build.sjsJUnitTests
2628

2729
val `sbt-dotty` = Build.`sbt-dotty`
2830
val `vscode-dotty` = Build.`vscode-dotty`

project/Build.scala

+137-23
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,35 @@ import sbtbuildinfo.BuildInfoPlugin.autoImport._
2828

2929
import scala.util.Properties.isJavaAtLeast
3030

31+
object MyScalaJSPlugin extends AutoPlugin {
32+
import Build._
33+
34+
override def requires: Plugins = ScalaJSPlugin
35+
36+
override def projectSettings: Seq[Setting[_]] = Def.settings(
37+
commonBootstrappedSettings,
38+
39+
/* Remove the Scala.js compiler plugin for scalac, and enable the
40+
* Scala.js back-end of dotty instead.
41+
*/
42+
libraryDependencies := {
43+
val deps = libraryDependencies.value
44+
deps.filterNot(_.name.startsWith("scalajs-compiler")).map(_.withDottyCompat(scalaVersion.value))
45+
},
46+
scalacOptions += "-scalajs",
47+
48+
// Replace the JVM JUnit dependency by the Scala.js one
49+
libraryDependencies ~= {
50+
_.filter(!_.name.startsWith("junit-interface"))
51+
},
52+
libraryDependencies +=
53+
("org.scala-js" %% "scalajs-junit-test-runtime" % scalaJSVersion % "test").withDottyCompat(scalaVersion.value),
54+
55+
// Typecheck the Scala.js IR found on the classpath
56+
scalaJSLinkerConfig ~= (_.withCheckIR(true)),
57+
)
58+
}
59+
3160
object Build {
3261
val scalacVersion = "2.12.8"
3362
val referenceVersion = "0.14.0-RC1"
@@ -101,6 +130,8 @@ object Build {
101130
val ideTestsCompilerArguments = taskKey[Seq[String]]("Compiler arguments to use in IDE tests")
102131
val ideTestsDependencyClasspath = taskKey[Seq[File]]("Dependency classpath to use in IDE tests")
103132

133+
val fetchScalaJSSource = taskKey[File]("Fetch the sources of Scala.js")
134+
104135
lazy val SourceDeps = config("sourcedeps")
105136

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

746+
/** The dotty standard library compiled with the Scala.js back-end, to produce
747+
* the corresponding .sjsir files.
748+
*
749+
* This artifact must be on the classpath on every "Dotty.js" project.
750+
*
751+
* Currently, only a very small fraction of the dotty library is actually
752+
* included in this project, and hence available to Dotty.js projects. More
753+
* will be added in the future as things are confirmed to be supported.
754+
*/
755+
lazy val `dotty-library-bootstrappedJS`: Project = project.in(file("library-js")).
756+
asDottyLibrary(Bootstrapped).
757+
enablePlugins(MyScalaJSPlugin).
758+
settings(
759+
unmanagedSourceDirectories in Compile :=
760+
(unmanagedSourceDirectories in (`dotty-library-bootstrapped`, Compile)).value,
761+
)
762+
715763
lazy val `dotty-sbt-bridge` = project.in(file("sbt-bridge/src")).
716764
// We cannot depend on any bootstrapped project to compile the bridge, since the
717765
// bridge is needed to compile these projects.
@@ -817,36 +865,102 @@ object Build {
817865
* useful, as that would not provide the linker and JS runners.
818866
*/
819867
lazy val sjsSandbox = project.in(file("sandbox/scalajs")).
820-
enablePlugins(ScalaJSPlugin).
821-
dependsOn(dottyLibrary(Bootstrapped)).
822-
settings(commonBootstrappedSettings).
868+
enablePlugins(MyScalaJSPlugin).
869+
dependsOn(`dotty-library-bootstrappedJS`).
823870
settings(
824-
/* Remove the Scala.js compiler plugin for scalac, and enable the
825-
* Scala.js back-end of dotty instead.
826-
*/
827-
libraryDependencies := {
828-
val deps = libraryDependencies.value
829-
deps.filterNot(_.name.startsWith("scalajs-compiler")).map(_.withDottyCompat(scalaVersion.value))
830-
},
831-
scalacOptions += "-scalajs",
871+
scalaJSUseMainModuleInitializer := true,
872+
)
873+
874+
/** Scala.js test suite.
875+
*
876+
* This project downloads the sources of the upstream Scala.js test suite,
877+
* and tests them with the dotty Scala.js back-end. Currently, only a very
878+
* small fraction of the upstream test suite is actually compiled and run.
879+
* It will grow in the future, as more stuff is confirmed to be supported.
880+
*/
881+
lazy val sjsJUnitTests = project.in(file("tests/sjs-junit")).
882+
enablePlugins(MyScalaJSPlugin).
883+
dependsOn(`dotty-library-bootstrappedJS`).
884+
settings(
885+
scalacOptions --= Seq("-Xfatal-warnings", "-deprecation"),
886+
887+
sourceDirectory in fetchScalaJSSource := target.value / s"scala-js-src-$scalaJSVersion",
888+
889+
fetchScalaJSSource := {
890+
import org.eclipse.jgit.api._
891+
892+
val s = streams.value
893+
val ver = scalaJSVersion
894+
val trgDir = (sourceDirectory in fetchScalaJSSource).value
895+
896+
if (!trgDir.exists) {
897+
s.log.info(s"Fetching Scala.js source version $ver")
898+
IO.createDirectory(trgDir)
899+
new CloneCommand()
900+
.setDirectory(trgDir)
901+
.setURI("https://github.com/scala-js/scala-js.git")
902+
.call()
903+
}
904+
905+
// Checkout proper ref. We do this anyway so we fail if something is wrong
906+
val git = Git.open(trgDir)
907+
s.log.info(s"Checking out Scala.js source version $ver")
908+
git.checkout().setName(s"v$ver").call()
832909

833-
// Replace the JVM JUnit dependency by the Scala.js one
834-
libraryDependencies ~= {
835-
_.filter(!_.name.startsWith("junit-interface"))
910+
trgDir
836911
},
912+
913+
// We need JUnit in the Compile configuration
837914
libraryDependencies +=
838-
("org.scala-js" %% "scalajs-junit-test-runtime" % scalaJSVersion % "test").withDottyCompat(scalaVersion.value),
915+
("org.scala-js" %% "scalajs-junit-test-runtime" % scalaJSVersion).withDottyCompat(scalaVersion.value),
839916

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

843-
scalaJSUseMainModuleInitializer := true,
920+
val stage = scalaJSStage.value
844921

845-
/* Debug-friendly Scala.js optimizer options.
846-
* In particular, typecheck the Scala.js IR found on the classpath.
847-
*/
848-
scalaJSLinkerConfig ~= {
849-
_.withCheckIR(true).withParallel(false)
922+
val linkerConfig = stage match {
923+
case FastOptStage => (scalaJSLinkerConfig in (Compile, fastOptJS)).value
924+
case FullOptStage => (scalaJSLinkerConfig in (Compile, fullOptJS)).value
925+
}
926+
927+
val moduleKind = linkerConfig.moduleKind
928+
val sems = linkerConfig.semantics
929+
930+
ConstantHolderGenerator.generate(
931+
(sourceManaged in Compile).value,
932+
"org.scalajs.testsuite.utils.BuildInfo",
933+
"scalaVersion" -> scalaVersion.value,
934+
"hasSourceMaps" -> false, //MyScalaJSPlugin.wantSourceMaps.value,
935+
"isNoModule" -> (moduleKind == ModuleKind.NoModule),
936+
"isESModule" -> (moduleKind == ModuleKind.ESModule),
937+
"isCommonJSModule" -> (moduleKind == ModuleKind.CommonJSModule),
938+
"isFullOpt" -> (stage == FullOptStage),
939+
"compliantAsInstanceOfs" -> (sems.asInstanceOfs == CheckedBehavior.Compliant),
940+
"compliantArrayIndexOutOfBounds" -> (sems.arrayIndexOutOfBounds == CheckedBehavior.Compliant),
941+
"compliantModuleInit" -> (sems.moduleInit == CheckedBehavior.Compliant),
942+
"strictFloats" -> sems.strictFloats,
943+
"productionMode" -> sems.productionMode,
944+
"es2015" -> linkerConfig.esFeatures.useECMAScript2015,
945+
)
946+
}.taskValue,
947+
948+
managedSources in Compile ++= {
949+
val dir = fetchScalaJSSource.value / "test-suite/js/src/main/scala"
950+
val filter = (
951+
("*.scala": FileFilter)
952+
-- "Typechecking*.scala"
953+
-- "NonNativeTypeTestSeparateRun.scala"
954+
)
955+
(dir ** filter).get
956+
},
957+
958+
managedSources in Test ++= {
959+
val dir = fetchScalaJSSource.value / "test-suite"
960+
(
961+
(dir / "shared/src/test/scala/org/scalajs/testsuite/compiler" ** "IntTest.scala").get
962+
++ (dir / "shared/src/test/scala/org/scalajs/testsuite/utils" ** "*.scala").get
963+
)
850964
}
851965
)
852966

project/ConstantHolderGenerator.scala

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import scala.annotation.tailrec
2+
3+
import sbt._
4+
5+
import org.scalajs.ir.ScalaJSVersions
6+
7+
object ConstantHolderGenerator {
8+
/** Generate a *.scala file that contains the given values as literals. */
9+
def generate(dir: File, fqn: String, values: (String, Any)*): Seq[File] = {
10+
val (fullPkg@(_ :+ pkg)) :+ objectName = fqn.split('.').toSeq
11+
12+
val out = dir / (objectName + ".scala")
13+
14+
val defs = for {
15+
(name, value) <- values
16+
} yield {
17+
s"val $name = ${literal(value)}"
18+
}
19+
20+
val scalaCode =
21+
s"""
22+
package ${fullPkg.mkString(".")}
23+
24+
private[$pkg] object $objectName {
25+
${defs.mkString("\n")}
26+
}
27+
"""
28+
29+
IO.write(out, scalaCode)
30+
31+
Seq(out)
32+
}
33+
34+
@tailrec
35+
private final def literal(v: Any): String = v match {
36+
case s: String => "raw\"\"\"" + s + "\"\"\""
37+
case b: Boolean => b.toString
38+
case f: File => literal(f.getAbsolutePath)
39+
40+
case _ =>
41+
throw new IllegalArgumentException(
42+
"Unsupported value type: " + v.getClass)
43+
}
44+
}

0 commit comments

Comments
 (0)