From 99dc09f68a2fbd3b474e50e1203b49a95ae75923 Mon Sep 17 00:00:00 2001 From: Tomasz Godzik Date: Mon, 19 Jun 2023 14:43:21 +0200 Subject: [PATCH] refactor: Move scalacli tests to a separate job Previously, we would run the Scala CLI tests together with normal unit tests, but that could cause OOM issues on the CI since we would efectively start 2 Bloop instances. Now, we run the ScalaCLI tests separately. Also: - updated tests to run in Scala 3 - updated the version of ScalaCLI I did those together since there is an issue with scripts alongside scala files on Scala 2, but it doesn't make sense to block on this as the version we use is only used for single scripts (which works well) and anyway the users will most likely have the Scala CLI version installed locally. --- .github/workflows/ci.yml | 5 + project/V.scala | 2 +- .../scalacli/BaseScalaCLIActionSuite.scala | 62 ++++ .../tests/scalacli/BaseScalaCliSuite.scala | 166 ++++++++++ .../tests/scalacli/ScalaCliBuildLayout.scala | 17 ++ .../tests/feature/CrossCodeLensLspSuite.scala | 29 -- .../BreakpointScalaCliDapSuite.scala | 3 +- .../scalacli/CodeLensesScalaCliSuite.scala | 38 +++ ...ifyScalaCliDependencyCodeActionSuite.scala | 15 +- .../scalacli}/ScalaCliActionsSuite.scala | 10 +- .../scalacli/ScalaCliCodeLensesSuite.scala | 53 ++++ .../tests/scalacli/ScalaCliDebugSuite.scala | 45 +++ .../scala/tests/scalacli/ScalaCliSuite.scala} | 287 +++++------------- .../main/scala/tests/BuildServerLayout.scala | 16 +- .../codeactions/BaseCodeActionLspSuite.scala | 28 +- .../test/scala/tests/CodeLensLspSuite.scala | 46 --- .../scala/tests/DebugDiscoverySuite.scala | 27 -- .../src/test/scala/tests/ScalaCliSuite.scala | 5 - 18 files changed, 479 insertions(+), 375 deletions(-) create mode 100644 tests/slow/src/main/scala/tests/scalacli/BaseScalaCLIActionSuite.scala create mode 100644 tests/slow/src/main/scala/tests/scalacli/BaseScalaCliSuite.scala create mode 100644 tests/slow/src/main/scala/tests/scalacli/ScalaCliBuildLayout.scala rename tests/{unit/src/test/scala/tests/debug => slow/src/test/scala/tests/scalacli}/BreakpointScalaCliDapSuite.scala (96%) create mode 100644 tests/slow/src/test/scala/tests/scalacli/CodeLensesScalaCliSuite.scala rename tests/{unit/src/test/scala/tests/codeactions => slow/src/test/scala/tests/scalacli}/MillifyScalaCliDependencyCodeActionSuite.scala (85%) rename tests/{unit/src/test/scala/tests/codeactions => slow/src/test/scala/tests/scalacli}/ScalaCliActionsSuite.scala (92%) create mode 100644 tests/slow/src/test/scala/tests/scalacli/ScalaCliCodeLensesSuite.scala create mode 100644 tests/slow/src/test/scala/tests/scalacli/ScalaCliDebugSuite.scala rename tests/{unit/src/main/scala/tests/BaseScalaCliSuite.scala => slow/src/test/scala/tests/scalacli/ScalaCliSuite.scala} (51%) delete mode 100644 tests/unit/src/test/scala/tests/ScalaCliSuite.scala diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d23d3cb69f4..53ae2583afd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,6 +57,7 @@ jobs: sbt, maven, gradle, + scalacli, mill, feature, cross, @@ -89,6 +90,10 @@ jobs: command: bin/test.sh 'slow/testOnly -- tests.mill.*' name: Mill integration os: ubuntu-latest + - type: scalacli + command: bin/test.sh 'slow/testOnly -- tests.scalacli.*' + name: Scala CLI integration + os: ubuntu-latest - type: feature command: bin/test.sh 'slow/testOnly -- tests.feature.*' name: LSP integration tests diff --git a/project/V.scala b/project/V.scala index 54a4ff155d0..6cc478c1267 100644 --- a/project/V.scala +++ b/project/V.scala @@ -34,7 +34,7 @@ object V { val pprint = "0.7.3" val sbtBloop = bloop val sbtJdiTools = "1.1.1" - val scalaCli = "0.2.1" + val scalaCli = "1.0.1" val scalafix = "0.11.0" val scalafmt = "3.7.4" val scalameta = "4.7.8" diff --git a/tests/slow/src/main/scala/tests/scalacli/BaseScalaCLIActionSuite.scala b/tests/slow/src/main/scala/tests/scalacli/BaseScalaCLIActionSuite.scala new file mode 100644 index 00000000000..2db12d19a3f --- /dev/null +++ b/tests/slow/src/main/scala/tests/scalacli/BaseScalaCLIActionSuite.scala @@ -0,0 +1,62 @@ +package tests.scalacli + +import munit.Location +import munit.TestOptions +import org.eclipse.lsp4j.CodeAction +import tests.codeactions.BaseCodeActionLspSuite + +class BaseScalaCLIActionSuite(name: String) + extends BaseCodeActionLspSuite(name) { + + def checkScalaCLI( + name: TestOptions, + input: String, + expectedActions: String, + expectedCode: String, + selectedActionIndex: Int = 0, + expectNoDiagnostics: Boolean = true, + kind: List[String] = Nil, + scalafixConf: String = "", + scalacOptions: List[String] = Nil, + scalaCliOptions: List[String] = Nil, + configuration: => Option[String] = None, + scalaVersion: String = scalaVersion, + renamePath: Option[String] = None, + extraOperations: => Unit = (), + fileName: String = "A.scala", + changeFile: String => String = identity, + expectError: Boolean = false, + filterAction: CodeAction => Boolean = _ => true, + )(implicit loc: Location): Unit = { + + val path = toPath(fileName) + val layout = Some( + s"""/.bsp/scala-cli.json + |${BaseScalaCliSuite.scalaCliBspJsonContent(scalaCliOptions)} + |/.scala-build/ide-inputs.json + |${BaseScalaCliSuite.scalaCliIdeInputJson(".")} + |/$path + |$input""".stripMargin + ) + super.check( + name, + input, + expectedActions, + expectedCode, + selectedActionIndex, + expectNoDiagnostics, + kind, + scalafixConf, + scalacOptions, + configuration, + scalaVersion, + renamePath, + extraOperations, + fileName, + changeFile, + expectError, + filterAction, + overrideLayout = layout, + ) + } +} diff --git a/tests/slow/src/main/scala/tests/scalacli/BaseScalaCliSuite.scala b/tests/slow/src/main/scala/tests/scalacli/BaseScalaCliSuite.scala new file mode 100644 index 00000000000..927f3ea140d --- /dev/null +++ b/tests/slow/src/main/scala/tests/scalacli/BaseScalaCliSuite.scala @@ -0,0 +1,166 @@ +package tests.scalacli + +import java.io.File +import java.nio.file.Files +import java.util.concurrent.Executors +import java.util.concurrent.TimeoutException + +import scala.concurrent.Future +import scala.concurrent.Promise +import scala.concurrent.duration._ + +import scala.meta.internal.metals.BuildInfo +import scala.meta.internal.metals.Messages +import scala.meta.internal.metals.scalacli.ScalaCli + +import ch.epfl.scala.bsp4j.MessageType +import org.eclipse.lsp4j.InitializeResult +import org.eclipse.lsp4j.MessageActionItem +import tests.BaseLspSuite +import tests.ScriptsAssertions + +abstract class BaseScalaCliSuite(protected val scalaVersion: String) + extends BaseLspSuite(s"scala-cli-$scalaVersion") + with ScriptsAssertions { + + private val scheduler = Executors.newSingleThreadScheduledExecutor() + + private def timeout( + message: String, + duration: FiniteDuration, + ): Future[Unit] = { + val p = Promise[Unit]() + val r: Runnable = { () => + p.failure(new TimeoutException(message)) + } + scheduler.schedule(r, duration.length, duration.unit) + p.future + } + + override def afterAll(): Unit = { + super.afterAll() + scheduler.shutdown() + } + + override def munitIgnore: Boolean = + !isValidScalaVersionForEnv(scalaVersion) + + private var importedPromise = Promise[Unit]() + + override def newServer(workspaceName: String): Unit = { + super.newServer(workspaceName) + val previousShowMessageHandler = server.client.showMessageHandler + server.client.showMessageHandler = { params => + if (params == Messages.ImportScalaScript.ImportedScalaCli) + importedPromise.success(()) + else if ( + params.getType == MessageType.ERROR && params.getMessage.startsWith( + "Error importing Scala CLI project " + ) + ) + importedPromise.failure( + new Exception(s"Error importing project: $params") + ) + else + previousShowMessageHandler(params) + } + val previousShowMessageRequestHandler = + server.client.showMessageRequestHandler + server.client.showMessageRequestHandler = { params => + def useBsp = Files.exists( + server.server.folder + .resolve(".bsp/scala-cli.json") + .toNIO + ) + if (params == Messages.ImportScalaScript.params()) + Some( + new MessageActionItem( + if (useBsp) + Messages.ImportScalaScript.dismiss + else + Messages.ImportScalaScript.doImportScalaCli + ) + ) + else if (params == Messages.ImportAllScripts.params()) + Some( + new MessageActionItem( + if (useBsp) + Messages.ImportAllScripts.dismiss + else + Messages.ImportAllScripts.importAll + ) + ) + else + previousShowMessageRequestHandler(params) + } + } + + protected def manualLayout: String = + s"""/metals.json + |{ + | "a": { + | "scalaVersion": "$scalaVersion" + | } + |} + | + |""".stripMargin + + protected def bspLayout: String = + s"""/.bsp/scala-cli.json + |${BaseScalaCliSuite.scalaCliBspJsonContent()} + | + |/.scala-build/ide-inputs.json + |${BaseScalaCliSuite.scalaCliIdeInputJson(".")} + | + |""".stripMargin + + protected def scalaCliInitialize( + useBsp: Boolean + )(layout: String): Future[InitializeResult] = { + if (!useBsp) + importedPromise = Promise[Unit]() + initialize( + (if (useBsp) bspLayout else manualLayout) + layout + ) + } + + protected def waitForImport(useBsp: Boolean): Future[Unit] = + if (useBsp) Future.successful(()) + else + Future + .firstCompletedOf( + List( + importedPromise.future, + timeout("import timeout", 180.seconds), + ) + ) + +} + +object BaseScalaCliSuite { + def scalaCliBspJsonContent(args: List[String] = Nil): String = { + val argv = List( + ScalaCli.javaCommand, + "-cp", + ScalaCli.scalaCliClassPath().mkString(File.pathSeparator), + ScalaCli.scalaCliMainClass, + "bsp", + ".", + ) ++ args + val bsjJson = ujson.Obj( + "name" -> "scala-cli", + "argv" -> argv, + "version" -> BuildInfo.scalaCliVersion, + "bspVersion" -> "2.0.0", + "languages" -> List("scala", "java"), + ) + ujson.write(bsjJson) + } + + def scalaCliIdeInputJson(args: String*): String = { + val ideInputJson = ujson.Obj( + "args" -> args + ) + ujson.write(ideInputJson) + } +} diff --git a/tests/slow/src/main/scala/tests/scalacli/ScalaCliBuildLayout.scala b/tests/slow/src/main/scala/tests/scalacli/ScalaCliBuildLayout.scala new file mode 100644 index 00000000000..ee4241c5759 --- /dev/null +++ b/tests/slow/src/main/scala/tests/scalacli/ScalaCliBuildLayout.scala @@ -0,0 +1,17 @@ +package tests.scalacli + +import tests.BuildToolLayout + +object ScalaCliBuildLayout extends BuildToolLayout { + override def apply( + sourceLayout: String, + scalaVersion: String, + ): String = { + s"""/.bsp/scala-cli.json + |${BaseScalaCliSuite.scalaCliBspJsonContent(List("-S", scalaVersion))} + |/.scala-build/ide-inputs.json + |${BaseScalaCliSuite.scalaCliIdeInputJson(".")} + |$sourceLayout + |""".stripMargin + } +} diff --git a/tests/slow/src/test/scala/tests/feature/CrossCodeLensLspSuite.scala b/tests/slow/src/test/scala/tests/feature/CrossCodeLensLspSuite.scala index 363a9cec13e..1d489ad5488 100644 --- a/tests/slow/src/test/scala/tests/feature/CrossCodeLensLspSuite.scala +++ b/tests/slow/src/test/scala/tests/feature/CrossCodeLensLspSuite.scala @@ -3,7 +3,6 @@ package tests.feature import scala.meta.internal.metals.{BuildInfo => V} import tests.BaseCodeLensLspSuite -import tests.ScalaCliBuildLayout class CrossCodeLensLspSuite extends BaseCodeLensLspSuite("cross-code-lens") { @@ -118,32 +117,4 @@ class CrossCodeLensLspSuite extends BaseCodeLensLspSuite("cross-code-lens") { } yield () } - test("run-main-annotation-with-script") { - cleanWorkspace() - val path = "main.sc" - for { - _ <- initialize( - ScalaCliBuildLayout( - s"""|/$path - |val x = 3 - | - |@main def main() = { - | println("annotation") - |}""".stripMargin, - V.scala3, - ) - ) - _ <- server.didOpen(path) - _ <- assertCodeLenses( - path, - """|<><> - |val x = 3 - | - |<><> - |@main def main() = { - | println("annotation") - |}""".stripMargin, - ) - } yield () - } } diff --git a/tests/unit/src/test/scala/tests/debug/BreakpointScalaCliDapSuite.scala b/tests/slow/src/test/scala/tests/scalacli/BreakpointScalaCliDapSuite.scala similarity index 96% rename from tests/unit/src/test/scala/tests/debug/BreakpointScalaCliDapSuite.scala rename to tests/slow/src/test/scala/tests/scalacli/BreakpointScalaCliDapSuite.scala index 2ca6a23472e..9f32911a12e 100644 --- a/tests/unit/src/test/scala/tests/debug/BreakpointScalaCliDapSuite.scala +++ b/tests/slow/src/test/scala/tests/scalacli/BreakpointScalaCliDapSuite.scala @@ -1,11 +1,10 @@ -package tests.debug +package tests.scalacli import scala.meta.internal.metals.DebugUnresolvedMainClassParams import scala.meta.internal.metals.JsonParser._ import tests.BaseDapSuite import tests.QuickBuildInitializer -import tests.ScalaCliBuildLayout class BreakpointScalaCliDapSuite extends BaseDapSuite( diff --git a/tests/slow/src/test/scala/tests/scalacli/CodeLensesScalaCliSuite.scala b/tests/slow/src/test/scala/tests/scalacli/CodeLensesScalaCliSuite.scala new file mode 100644 index 00000000000..6fd27408f4f --- /dev/null +++ b/tests/slow/src/test/scala/tests/scalacli/CodeLensesScalaCliSuite.scala @@ -0,0 +1,38 @@ +package tests.scalacli + +import scala.meta.internal.metals.{BuildInfo => V} + +import tests.BaseCodeLensLspSuite + +class CodeLensesScalaCliSuite + extends BaseCodeLensLspSuite("cross-code-lens-scalacli") { + + test("run-main-annotation-with-script") { + cleanWorkspace() + val path = "main.sc" + for { + _ <- initialize( + ScalaCliBuildLayout( + s"""|/$path + |val x = 3 + | + |def main() = { + | println("annotation") + |}""".stripMargin, + V.scala3, + ) + ) + _ <- server.didOpen(path) + _ <- assertCodeLenses( + path, + """|<><> + |val x = 3 + | + |def main() = { + | println("annotation") + |}""".stripMargin, + ) + } yield () + } + +} diff --git a/tests/unit/src/test/scala/tests/codeactions/MillifyScalaCliDependencyCodeActionSuite.scala b/tests/slow/src/test/scala/tests/scalacli/MillifyScalaCliDependencyCodeActionSuite.scala similarity index 85% rename from tests/unit/src/test/scala/tests/codeactions/MillifyScalaCliDependencyCodeActionSuite.scala rename to tests/slow/src/test/scala/tests/scalacli/MillifyScalaCliDependencyCodeActionSuite.scala index 10e0caba0e9..ebdaea59e98 100644 --- a/tests/unit/src/test/scala/tests/codeactions/MillifyScalaCliDependencyCodeActionSuite.scala +++ b/tests/slow/src/test/scala/tests/scalacli/MillifyScalaCliDependencyCodeActionSuite.scala @@ -1,16 +1,16 @@ -package tests.codeactions +package tests.scalacli import scala.meta.internal.metals.BuildInfo class MillifyScalaCliDependencyCodeActionSuite - extends BaseCodeActionLspSuite("millifyScalaCliDependency") { + extends BaseScalaCLIActionSuite("millifyScalaCliDependency") { val sbtStyleDependency = """"org.scalameta" %% "munit" % "0.7.26"""" val convertedDependency = """"org.scalameta::munit:0.7.26"""" val convertTo: String = s"""//> using lib $convertedDependency""" - check( + checkScalaCLI( "convert-dependency", s"""|//> <<>>using lib $sbtStyleDependency | @@ -27,13 +27,12 @@ class MillifyScalaCliDependencyCodeActionSuite |""".stripMargin, scalaCliOptions = List("--actions", "-S", scalaVersion), expectNoDiagnostics = false, - scalaCliLayout = true, ) val sbtStyleDependencyMultiSpace = """ "org.scalameta" %% "munit" % "0.7.26"""" - check( + checkScalaCLI( "convert-dependency-multiple-whitespace", s"""|//> <<>>using lib $sbtStyleDependencyMultiSpace | @@ -48,12 +47,10 @@ class MillifyScalaCliDependencyCodeActionSuite | println("Hello") |} |""".stripMargin, - scalaCliOptions = List("--actions", "-S", scalaVersion), expectNoDiagnostics = false, - scalaCliLayout = true, ) - check( + checkScalaCLI( "convert-dependency-multiple", s"""|//> using scala "${BuildInfo.scala213}" |//> <<>>using lib $sbtStyleDependencyMultiSpace @@ -70,9 +67,7 @@ class MillifyScalaCliDependencyCodeActionSuite | println("Hello") |} |""".stripMargin, - scalaCliOptions = List("--actions", "-S", scalaVersion), expectNoDiagnostics = false, - scalaCliLayout = true, ) } diff --git a/tests/unit/src/test/scala/tests/codeactions/ScalaCliActionsSuite.scala b/tests/slow/src/test/scala/tests/scalacli/ScalaCliActionsSuite.scala similarity index 92% rename from tests/unit/src/test/scala/tests/codeactions/ScalaCliActionsSuite.scala rename to tests/slow/src/test/scala/tests/scalacli/ScalaCliActionsSuite.scala index 51e34820635..8534289ae90 100644 --- a/tests/unit/src/test/scala/tests/codeactions/ScalaCliActionsSuite.scala +++ b/tests/slow/src/test/scala/tests/scalacli/ScalaCliActionsSuite.scala @@ -1,4 +1,4 @@ -package tests.codeactions +package tests.scalacli import scala.meta.internal.metals.BuildInfo import scala.meta.internal.metals.codeactions.CreateNewSymbol @@ -9,7 +9,7 @@ import scala.meta.internal.mtags.CoursierComplete import coursier.version.Version class ScalaCliActionsSuite - extends BaseCodeActionLspSuite("actionableDiagnostic") { + extends BaseScalaCLIActionSuite("actionableDiagnostic") { val oldOsLibVersion: Version = Version("0.7.8") val coursierComplete = new CoursierComplete(scalaCompilerVersion) @@ -18,7 +18,7 @@ class ScalaCliActionsSuite .headOption .getOrElse("0.8.1") - check( + checkScalaCLI( "actionable-diagnostic-update", s"""|//> <<>>using lib "com.lihaoyi::os-lib:${oldOsLibVersion.repr}" | @@ -35,10 +35,9 @@ class ScalaCliActionsSuite |""".stripMargin, scalaCliOptions = List("--actions", "-S", scalaVersion), expectNoDiagnostics = false, - scalaCliLayout = true, ) - check( + checkScalaCLI( "auto-import", s"""|//> using scala "${BuildInfo.scala213}" |//> using lib "org.typelevel::cats-core:2.9.0" @@ -61,7 +60,6 @@ class ScalaCliActionsSuite |""".stripMargin, scalaCliOptions = List("--actions", "-S", scalaVersion), expectNoDiagnostics = false, - scalaCliLayout = true, fileName = "A.sc", ) diff --git a/tests/slow/src/test/scala/tests/scalacli/ScalaCliCodeLensesSuite.scala b/tests/slow/src/test/scala/tests/scalacli/ScalaCliCodeLensesSuite.scala new file mode 100644 index 00000000000..1da30aa2e4f --- /dev/null +++ b/tests/slow/src/test/scala/tests/scalacli/ScalaCliCodeLensesSuite.scala @@ -0,0 +1,53 @@ +package tests.scalacli + +import tests.BaseCodeLensLspSuite + +class ScalaCliCodeLensesSuite + extends BaseCodeLensLspSuite("scala-cli-code-lenses") { + + private val scalaCliScriptPath = "a/src/main/scala/a/main.sc" + test("run-script") { + cleanWorkspace() + for { + + _ <- initialize( + s"""/.bsp/scala-cli.json + |${BaseScalaCliSuite.scalaCliBspJsonContent()} + |/.scala-build/ide-inputs.json + |${BaseScalaCliSuite.scalaCliIdeInputJson(".")} + |/$scalaCliScriptPath + |print("oranges are nice")""".stripMargin + ) + _ <- server.didOpen(scalaCliScriptPath) + _ <- assertCodeLenses( + scalaCliScriptPath, + """|<><> + |print("oranges are nice") + |""".stripMargin, + ) + } yield () + } + + private val scalaCliScriptPathTop = "main.sc" + test("run-script-top") { + cleanWorkspace() + for { + + _ <- initialize( + s"""/.bsp/scala-cli.json + |${BaseScalaCliSuite.scalaCliBspJsonContent()} + |/.scala-build/ide-inputs.json + |${BaseScalaCliSuite.scalaCliIdeInputJson(".")} + |/$scalaCliScriptPathTop + |print("oranges are nice")""".stripMargin + ) + _ <- server.didOpen(scalaCliScriptPathTop) + _ <- assertCodeLenses( + scalaCliScriptPathTop, + """|<><> + |print("oranges are nice") + |""".stripMargin, + ) + } yield () + } +} diff --git a/tests/slow/src/test/scala/tests/scalacli/ScalaCliDebugSuite.scala b/tests/slow/src/test/scala/tests/scalacli/ScalaCliDebugSuite.scala new file mode 100644 index 00000000000..a497e1d855b --- /dev/null +++ b/tests/slow/src/test/scala/tests/scalacli/ScalaCliDebugSuite.scala @@ -0,0 +1,45 @@ +package tests.scalacli + +import java.util.concurrent.TimeUnit + +import scala.meta.internal.metals.DebugDiscoveryParams +import scala.meta.internal.metals.JsonParser._ + +import tests.BaseDapSuite +import tests.QuickBuildInitializer + +class ScalaCliDebugSuite + extends BaseDapSuite( + "scala-cli-debug", + QuickBuildInitializer, + ScalaCliBuildLayout, + ) { + + private val scalaCliScriptPath = "a/src/main/scala/a/main.sc" + test("run-scala-cli-script") { + for { + _ <- initialize( + s"""/.bsp/scala-cli.json + |${BaseScalaCliSuite.scalaCliBspJsonContent()} + |/.scala-build/ide-inputs.json + |${BaseScalaCliSuite.scalaCliIdeInputJson(".")} + |/$scalaCliScriptPath + |print("oranges are nice")""".stripMargin + ) + _ <- server.didOpen(scalaCliScriptPath) + _ <- server.waitFor(TimeUnit.SECONDS.toMillis(10)) + debugger <- server.startDebuggingUnresolved( + DebugDiscoveryParams( + server.toPath(scalaCliScriptPath).toURI.toString, + "run", + ).toJson + ) + _ <- debugger.initialize + _ <- debugger.launch + _ <- debugger.configurationDone + _ <- debugger.shutdown + output <- debugger.allOutput + } yield assertNoDiff(output, "oranges are nice") + } + +} diff --git a/tests/unit/src/main/scala/tests/BaseScalaCliSuite.scala b/tests/slow/src/test/scala/tests/scalacli/ScalaCliSuite.scala similarity index 51% rename from tests/unit/src/main/scala/tests/BaseScalaCliSuite.scala rename to tests/slow/src/test/scala/tests/scalacli/ScalaCliSuite.scala index 8b91701dc5a..5020b25ef7f 100644 --- a/tests/unit/src/main/scala/tests/BaseScalaCliSuite.scala +++ b/tests/slow/src/test/scala/tests/scalacli/ScalaCliSuite.scala @@ -1,193 +1,15 @@ -package tests - -import java.io.File -import java.nio.file.Files -import java.util.concurrent.Executors -import java.util.concurrent.TimeoutException +package tests.scalacli import scala.concurrent.Future -import scala.concurrent.Promise -import scala.concurrent.duration._ -import scala.meta.internal.metals.BuildInfo -import scala.meta.internal.metals.Messages import scala.meta.internal.metals.ServerCommands -import scala.meta.internal.metals.scalacli.ScalaCli - -import ch.epfl.scala.bsp4j.MessageType -import org.eclipse.lsp4j.InitializeResult -import org.eclipse.lsp4j.MessageActionItem - -abstract class BaseScalaCliSuite(scalaVersion: String) - extends BaseLspSuite(s"scala-cli-$scalaVersion") - with ScriptsAssertions { - - private val scheduler = Executors.newSingleThreadScheduledExecutor() - - private def timeout( - message: String, - duration: FiniteDuration, - ): Future[Unit] = { - val p = Promise[Unit]() - val r: Runnable = { () => - p.failure(new TimeoutException(message)) - } - scheduler.schedule(r, duration.length, duration.unit) - p.future - } - - override def afterAll(): Unit = { - super.afterAll() - scheduler.shutdown() - } +import scala.meta.internal.metals.{BuildInfo => V} - override def munitIgnore: Boolean = - !isValidScalaVersionForEnv(scalaVersion) +import tests.FileLayout - private var importedPromise = Promise[Unit]() - - override def newServer(workspaceName: String): Unit = { - super.newServer(workspaceName) - val previousShowMessageHandler = server.client.showMessageHandler - server.client.showMessageHandler = { params => - if (params == Messages.ImportScalaScript.ImportedScalaCli) - importedPromise.success(()) - else if ( - params.getType == MessageType.ERROR && params.getMessage.startsWith( - "Error importing Scala CLI project " - ) - ) - importedPromise.failure( - new Exception(s"Error importing project: $params") - ) - else - previousShowMessageHandler(params) - } - val previousShowMessageRequestHandler = - server.client.showMessageRequestHandler - server.client.showMessageRequestHandler = { params => - def useBsp = Files.exists( - server.server.folder - .resolve(".bsp/scala-cli.json") - .toNIO - ) - if (params == Messages.ImportScalaScript.params()) - Some( - new MessageActionItem( - if (useBsp) - Messages.ImportScalaScript.dismiss - else - Messages.ImportScalaScript.doImportScalaCli - ) - ) - else if (params == Messages.ImportAllScripts.params()) - Some( - new MessageActionItem( - if (useBsp) - Messages.ImportAllScripts.dismiss - else - Messages.ImportAllScripts.importAll - ) - ) - else - previousShowMessageRequestHandler(params) - } - } - - private def manualLayout = - s"""/metals.json - |{ - | "a": { - | "scalaVersion": "$scalaVersion" - | } - |} - | - |""".stripMargin - private def bspLayout = - s"""/.bsp/scala-cli.json - |${BaseScalaCliSuite.scalaCliBspJsonContent()} - | - |/.scala-build/ide-inputs.json - |${BaseScalaCliSuite.scalaCliIdeInputJson(".")} - | - |""".stripMargin - - private def scalaCliInitialize( - useBsp: Boolean - )(layout: String): Future[InitializeResult] = { - if (!useBsp) - importedPromise = Promise[Unit]() - initialize( - (if (useBsp) bspLayout else manualLayout) + layout - ) - } - - private def waitForImport(useBsp: Boolean): Future[Unit] = - if (useBsp) Future.successful(()) - else - Future - .firstCompletedOf( - List( - importedPromise.future, - timeout("import timeout", 180.seconds), - ) - ) - - for (useBsp <- Seq(true, false)) { - val message = if (useBsp) "BSP" else "manual" - test(s"simple file $message") { - simpleFileTest(useBsp) - } - test(s"simple script $message") { - simpleScriptTest(useBsp) - } - } - - private val simpleFileLayout = - s"""|/MyTests.scala - |//> using scala "$scalaVersion" - |//> using lib "com.lihaoyi::utest::0.7.9" - |//> using lib "com.lihaoyi::pprint::0.6.4" - | - |import foo.Foo - |import utest._ - | - |object MyTests extends TestSuite { - | pprint.log(2) - | val tests = Tests { - | test("foo") { - | assert(2 + 2 == 4) - | } - | test("nope") { - | assert(2 + 2 == (new Foo).value) - | } - | } - |} - | - |/foo.sc - |class Foo { - | def value = 5 - |} - |""".stripMargin +class ScalaCliSuite extends BaseScalaCliSuite(V.scala3) { - test("connecting-scalacli") { - cleanWorkspace() - for { - _ <- server.initialize() - _ <- server.initialized() - _ = FileLayout.fromString(simpleFileLayout, workspace) - _ = FileLayout.fromString(bspLayout, workspace) - _ <- server.server.indexingPromise.future - _ <- server.didOpen("MyTests.scala") - _ <- assertDefinitionAtLocation( - "MyTests.scala", - "val tests = Test@@s", - "utest/Tests.scala", - ) - } yield () - } - - def simpleFileTest(useBsp: Boolean): Future[Unit] = + private def simpleFileTest(useBsp: Boolean): Future[Unit] = for { _ <- scalaCliInitialize(useBsp)(simpleFileLayout) _ <- server.didOpen("MyTests.scala") @@ -226,18 +48,18 @@ abstract class BaseScalaCliSuite(scalaVersion: String) } yield () - def simpleScriptTest(useBsp: Boolean): Future[Unit] = + private def simpleScriptTest(useBsp: Boolean): Future[Unit] = for { _ <- scalaCliInitialize(useBsp)( s"""/MyTests.sc |#!/usr/bin/env -S scala-cli shebang --java-opt -Xms256m --java-opt -XX:MaxRAMPercentage=80 |//> using scala "$scalaVersion" - |//> using lib "com.lihaoyi::utest::0.7.9" - |//> using lib "com.lihaoyi::pprint::0.6.4" + |//> using lib "com.lihaoyi::utest::0.7.10" + |//> using lib "com.lihaoyi::pprint::0.6.6" | |import foo.Foo |import utest._ - | + | |pprint.log(2) // top-level statement should be fine in a script | |object MyTests extends TestSuite { @@ -301,13 +123,73 @@ abstract class BaseScalaCliSuite(scalaVersion: String) } yield () + private val simpleFileLayout = + s"""|/MyTests.scala + |//> using scala "$scalaVersion" + |//> using lib "com.lihaoyi::utest::0.7.10" + |//> using lib "com.lihaoyi::pprint::0.6.6" + | + |import foo.Foo + |import utest._ + | + |object MyTests extends TestSuite { + | pprint.log(2) + | val tests = Tests { + | test("foo") { + | assert(2 + 2 == 4) + | } + | test("nope") { + | assert(2 + 2 == (new Foo).value) + | } + | } + |} + | + |/foo.sc + |class Foo { + | def value = 5 + |} + |""".stripMargin + + test(s"simple-file-bsp") { + simpleFileTest(useBsp = true) + } + + test(s"simple-file-manual") { + simpleFileTest(useBsp = false) + } + + test(s"simple-script-bsp") { + simpleScriptTest(useBsp = true) + } + + test(s"simple-script-manual") { + simpleScriptTest(useBsp = false) + } + + test("connecting-scalacli") { + cleanWorkspace() + for { + _ <- server.initialize() + _ <- server.initialized() + _ = FileLayout.fromString(simpleFileLayout, workspace) + _ = FileLayout.fromString(bspLayout, workspace) + _ <- server.server.indexingPromise.future + _ <- server.didOpen("MyTests.scala") + _ <- assertDefinitionAtLocation( + "MyTests.scala", + "val tests = Test@@s", + "utest/Tests.scala", + ) + } yield () + } + test("relative-semanticdb-root") { for { _ <- scalaCliInitialize(useBsp = false)( s"""/scripts/MyTests.scala |//> using scala "$scalaVersion" - |//> using lib "com.lihaoyi::utest::0.7.9" - |//> using lib "com.lihaoyi::pprint::0.6.4" + |//> using lib "com.lihaoyi::utest::0.7.10" + |//> using lib "com.lihaoyi::pprint::0.6.6" | |import foo.Foo |import utest._ @@ -342,32 +224,5 @@ abstract class BaseScalaCliSuite(scalaVersion: String) ) } yield () } -} -object BaseScalaCliSuite { - def scalaCliBspJsonContent(args: List[String] = Nil): String = { - val argv = List( - ScalaCli.javaCommand, - "-cp", - ScalaCli.scalaCliClassPath().mkString(File.pathSeparator), - ScalaCli.scalaCliMainClass, - "bsp", - ".", - ) ++ args - val bsjJson = ujson.Obj( - "name" -> "scala-cli", - "argv" -> argv, - "version" -> BuildInfo.scalaCliVersion, - "bspVersion" -> "2.0.0", - "languages" -> List("scala", "java"), - ) - ujson.write(bsjJson) - } - - def scalaCliIdeInputJson(args: String*): String = { - val ideInputJson = ujson.Obj( - "args" -> args - ) - ujson.write(ideInputJson) - } } diff --git a/tests/unit/src/main/scala/tests/BuildServerLayout.scala b/tests/unit/src/main/scala/tests/BuildServerLayout.scala index 3aa49922e31..1f4b7aea8bf 100644 --- a/tests/unit/src/main/scala/tests/BuildServerLayout.scala +++ b/tests/unit/src/main/scala/tests/BuildServerLayout.scala @@ -2,7 +2,7 @@ package tests import scala.meta.internal.metals.{BuildInfo => V} -sealed trait BuildToolLayout { +trait BuildToolLayout { def apply(sourceLayout: String, scalaVersion: String): String } @@ -20,20 +20,6 @@ object QuickBuildLayout extends BuildToolLayout { } } -object ScalaCliBuildLayout extends BuildToolLayout { - override def apply( - sourceLayout: String, - scalaVersion: String, - ): String = { - s"""/.bsp/scala-cli.json - |${BaseScalaCliSuite.scalaCliBspJsonContent(List("-S", scalaVersion))} - |/.scala-build/ide-inputs.json - |${BaseScalaCliSuite.scalaCliIdeInputJson(".")} - |$sourceLayout - |""".stripMargin - } -} - object SbtBuildLayout extends BuildToolLayout { val commonSbtSettings: String = """|import scala.concurrent.duration._ diff --git a/tests/unit/src/main/scala/tests/codeactions/BaseCodeActionLspSuite.scala b/tests/unit/src/main/scala/tests/codeactions/BaseCodeActionLspSuite.scala index 0e3c2b2a863..5d59d19dbfb 100644 --- a/tests/unit/src/main/scala/tests/codeactions/BaseCodeActionLspSuite.scala +++ b/tests/unit/src/main/scala/tests/codeactions/BaseCodeActionLspSuite.scala @@ -9,7 +9,6 @@ import munit.Location import munit.TestOptions import org.eclipse.lsp4j.CodeAction import tests.BaseLspSuite -import tests.BaseScalaCliSuite import tests.FileLayout abstract class BaseCodeActionLspSuite( @@ -39,6 +38,8 @@ abstract class BaseCodeActionLspSuite( ) } + protected def toPath(fileName: String): String = + s"a/src/main/scala/a/$fileName" def check( name: TestOptions, input: String, @@ -49,7 +50,6 @@ abstract class BaseCodeActionLspSuite( kind: List[String] = Nil, scalafixConf: String = "", scalacOptions: List[String] = Nil, - scalaCliOptions: List[String] = Nil, configuration: => Option[String] = None, scalaVersion: String = scalaVersion, renamePath: Option[String] = None, @@ -58,28 +58,20 @@ abstract class BaseCodeActionLspSuite( changeFile: String => String = identity, expectError: Boolean = false, filterAction: CodeAction => Boolean = _ => true, - scalaCliLayout: Boolean = false, + overrideLayout: Option[String] = None, )(implicit loc: Location): Unit = { val scalacOptionsJson = if (scalacOptions.nonEmpty) s""""scalacOptions": ["${scalacOptions.mkString("\",\"")}"],""" else "" - val path = s"a/src/main/scala/a/$fileName" + val path = toPath(fileName) - val layout = { - if (scalaCliLayout) - s"""/.bsp/scala-cli.json - |${BaseScalaCliSuite.scalaCliBspJsonContent(scalaCliOptions)} - |/.scala-build/ide-inputs.json - |${BaseScalaCliSuite.scalaCliIdeInputJson(".")} - |/$path - |$input""".stripMargin - else - s"""/metals.json - |{"a":{$scalacOptionsJson "scalaVersion" : "$scalaVersion"}} - |$scalafixConf - |/$path - |$input""".stripMargin + val layout = overrideLayout.getOrElse { + s"""/metals.json + |{"a":{$scalacOptionsJson "scalaVersion" : "$scalaVersion"}} + |$scalafixConf + |/$path + |$input""".stripMargin } checkEdit( diff --git a/tests/unit/src/test/scala/tests/CodeLensLspSuite.scala b/tests/unit/src/test/scala/tests/CodeLensLspSuite.scala index b65bf518e18..d43028ed34b 100644 --- a/tests/unit/src/test/scala/tests/CodeLensLspSuite.scala +++ b/tests/unit/src/test/scala/tests/CodeLensLspSuite.scala @@ -165,52 +165,6 @@ class CodeLensLspSuite extends BaseCodeLensLspSuite("codeLenses") { } yield () } - private val scalaCliScriptPath = "a/src/main/scala/a/main.sc" - test("run-script") { - cleanWorkspace() - for { - - _ <- initialize( - s"""/.bsp/scala-cli.json - |${BaseScalaCliSuite.scalaCliBspJsonContent()} - |/.scala-build/ide-inputs.json - |${BaseScalaCliSuite.scalaCliIdeInputJson(".")} - |/$scalaCliScriptPath - |print("oranges are nice")""".stripMargin - ) - _ <- server.didOpen(scalaCliScriptPath) - _ <- assertCodeLenses( - scalaCliScriptPath, - """|<><> - |print("oranges are nice") - |""".stripMargin, - ) - } yield () - } - - private val scalaCliScriptPathTop = "main.sc" - test("run-script-top") { - cleanWorkspace() - for { - - _ <- initialize( - s"""/.bsp/scala-cli.json - |${BaseScalaCliSuite.scalaCliBspJsonContent()} - |/.scala-build/ide-inputs.json - |${BaseScalaCliSuite.scalaCliIdeInputJson(".")} - |/$scalaCliScriptPathTop - |print("oranges are nice")""".stripMargin - ) - _ <- server.didOpen(scalaCliScriptPathTop) - _ <- assertCodeLenses( - scalaCliScriptPathTop, - """|<><> - |print("oranges are nice") - |""".stripMargin, - ) - } yield () - } - // Tests, whether main class in one project does not affect other class with same name in other project test("run-multi-module") { cleanWorkspace() diff --git a/tests/unit/src/test/scala/tests/DebugDiscoverySuite.scala b/tests/unit/src/test/scala/tests/DebugDiscoverySuite.scala index 2279db63864..cf5c181cdbb 100644 --- a/tests/unit/src/test/scala/tests/DebugDiscoverySuite.scala +++ b/tests/unit/src/test/scala/tests/DebugDiscoverySuite.scala @@ -24,7 +24,6 @@ class DebugDiscoverySuite private val fooPath = "a/src/main/scala/a/Foo.scala" private val barPath = "a/src/main/scala/a/Bar.scala" private val altTargetPath = "b/src/main/scala/b/Main.scala" - private val scalaCliScriptPath = "a/src/main/scala/a/main.sc" test("run") { for { @@ -92,32 +91,6 @@ class DebugDiscoverySuite } yield assertNoDiff(output, "oranges are nice") } - test("run-scala-cli-script") { - for { - _ <- initialize( - s"""/.bsp/scala-cli.json - |${BaseScalaCliSuite.scalaCliBspJsonContent()} - |/.scala-build/ide-inputs.json - |${BaseScalaCliSuite.scalaCliIdeInputJson(".")} - |/$scalaCliScriptPath - |print("oranges are nice")""".stripMargin - ) - _ <- server.didOpen(scalaCliScriptPath) - _ <- server.waitFor(TimeUnit.SECONDS.toMillis(10)) - debugger <- server.startDebuggingUnresolved( - DebugDiscoveryParams( - server.toPath(scalaCliScriptPath).toURI.toString, - "run", - ).toJson - ) - _ <- debugger.initialize - _ <- debugger.launch - _ <- debugger.configurationDone - _ <- debugger.shutdown - output <- debugger.allOutput - } yield assertNoDiff(output, "oranges are nice") - } - test("run-file-test") { for { _ <- initialize( diff --git a/tests/unit/src/test/scala/tests/ScalaCliSuite.scala b/tests/unit/src/test/scala/tests/ScalaCliSuite.scala deleted file mode 100644 index d39105d8d71..00000000000 --- a/tests/unit/src/test/scala/tests/ScalaCliSuite.scala +++ /dev/null @@ -1,5 +0,0 @@ -package tests - -import scala.meta.internal.metals.{BuildInfo => V} - -class ScalaCliSuite extends tests.BaseScalaCliSuite(V.scala213)