Skip to content

Commit 7c1a5d2

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 3d97515 commit 7c1a5d2

File tree

4 files changed

+192
-24
lines changed

4 files changed

+192
-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

+145-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,31 @@ 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+
// TODO Compile more (enventually all) of the library
760+
/*unmanagedSourceDirectories in Compile :=
761+
(unmanagedSourceDirectories in (`dotty-library-bootstrapped`, Compile)).value,*/
762+
sources in Compile := {
763+
val baseDir = (baseDirectory in (`dotty-library-bootstrapped`, Compile)).value
764+
Seq(
765+
baseDir / "src/scala/forceInline.scala",
766+
baseDir / "src-3.x/dotty/DottyPredef.scala",
767+
)
768+
},
769+
)
770+
715771
lazy val `dotty-sbt-bridge` = project.in(file("sbt-bridge/src")).
716772
// We cannot depend on any bootstrapped project to compile the bridge, since the
717773
// bridge is needed to compile these projects.
@@ -817,36 +873,102 @@ object Build {
817873
* useful, as that would not provide the linker and JS runners.
818874
*/
819875
lazy val sjsSandbox = project.in(file("sandbox/scalajs")).
820-
enablePlugins(ScalaJSPlugin).
821-
dependsOn(dottyLibrary(Bootstrapped)).
822-
settings(commonBootstrappedSettings).
876+
enablePlugins(MyScalaJSPlugin).
877+
dependsOn(`dotty-library-bootstrappedJS`).
823878
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",
879+
scalaJSUseMainModuleInitializer := true,
880+
)
832881

833-
// Replace the JVM JUnit dependency by the Scala.js one
834-
libraryDependencies ~= {
835-
_.filter(!_.name.startsWith("junit-interface"))
882+
/** Scala.js test suite.
883+
*
884+
* This project downloads the sources of the upstream Scala.js test suite,
885+
* and tests them with the dotty Scala.js back-end. Currently, only a very
886+
* small fraction of the upstream test suite is actually compiled and run.
887+
* It will grow in the future, as more stuff is confirmed to be supported.
888+
*/
889+
lazy val sjsJUnitTests = project.in(file("tests/sjs-junit")).
890+
enablePlugins(MyScalaJSPlugin).
891+
dependsOn(`dotty-library-bootstrappedJS`).
892+
settings(
893+
scalacOptions --= Seq("-Xfatal-warnings", "-deprecation"),
894+
895+
sourceDirectory in fetchScalaJSSource := target.value / s"scala-js-src-$scalaJSVersion",
896+
897+
fetchScalaJSSource := {
898+
import org.eclipse.jgit.api._
899+
900+
val s = streams.value
901+
val ver = scalaJSVersion
902+
val trgDir = (sourceDirectory in fetchScalaJSSource).value
903+
904+
if (!trgDir.exists) {
905+
s.log.info(s"Fetching Scala.js source version $ver")
906+
IO.createDirectory(trgDir)
907+
new CloneCommand()
908+
.setDirectory(trgDir)
909+
.setURI("https://github.com/scala-js/scala-js.git")
910+
.call()
911+
}
912+
913+
// Checkout proper ref. We do this anyway so we fail if something is wrong
914+
val git = Git.open(trgDir)
915+
s.log.info(s"Checking out Scala.js source version $ver")
916+
git.checkout().setName(s"v$ver").call()
917+
918+
trgDir
836919
},
920+
921+
// We need JUnit in the Compile configuration
837922
libraryDependencies +=
838-
("org.scala-js" %% "scalajs-junit-test-runtime" % scalaJSVersion % "test").withDottyCompat(scalaVersion.value),
923+
("org.scala-js" %% "scalajs-junit-test-runtime" % scalaJSVersion).withDottyCompat(scalaVersion.value),
839924

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

843-
scalaJSUseMainModuleInitializer := true,
928+
val stage = scalaJSStage.value
844929

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)
930+
val linkerConfig = stage match {
931+
case FastOptStage => (scalaJSLinkerConfig in (Compile, fastOptJS)).value
932+
case FullOptStage => (scalaJSLinkerConfig in (Compile, fullOptJS)).value
933+
}
934+
935+
val moduleKind = linkerConfig.moduleKind
936+
val sems = linkerConfig.semantics
937+
938+
ConstantHolderGenerator.generate(
939+
(sourceManaged in Compile).value,
940+
"org.scalajs.testsuite.utils.BuildInfo",
941+
"scalaVersion" -> scalaVersion.value,
942+
"hasSourceMaps" -> false, //MyScalaJSPlugin.wantSourceMaps.value,
943+
"isNoModule" -> (moduleKind == ModuleKind.NoModule),
944+
"isESModule" -> (moduleKind == ModuleKind.ESModule),
945+
"isCommonJSModule" -> (moduleKind == ModuleKind.CommonJSModule),
946+
"isFullOpt" -> (stage == FullOptStage),
947+
"compliantAsInstanceOfs" -> (sems.asInstanceOfs == CheckedBehavior.Compliant),
948+
"compliantArrayIndexOutOfBounds" -> (sems.arrayIndexOutOfBounds == CheckedBehavior.Compliant),
949+
"compliantModuleInit" -> (sems.moduleInit == CheckedBehavior.Compliant),
950+
"strictFloats" -> sems.strictFloats,
951+
"productionMode" -> sems.productionMode,
952+
"es2015" -> linkerConfig.esFeatures.useECMAScript2015,
953+
)
954+
}.taskValue,
955+
956+
managedSources in Compile ++= {
957+
val dir = fetchScalaJSSource.value / "test-suite/js/src/main/scala"
958+
val filter = (
959+
("*.scala": FileFilter)
960+
-- "Typechecking*.scala"
961+
-- "NonNativeTypeTestSeparateRun.scala"
962+
)
963+
(dir ** filter).get
964+
},
965+
966+
managedSources in Test ++= {
967+
val dir = fetchScalaJSSource.value / "test-suite"
968+
(
969+
(dir / "shared/src/test/scala/org/scalajs/testsuite/compiler" ** "IntTest.scala").get
970+
++ (dir / "shared/src/test/scala/org/scalajs/testsuite/utils" ** "*.scala").get
971+
)
850972
}
851973
)
852974

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)