Skip to content

Commit

Permalink
refactor: Move scalacli tests to a separate job
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
tgodzik committed Jun 20, 2023
1 parent d97fd1f commit 99dc09f
Show file tree
Hide file tree
Showing 18 changed files with 479 additions and 375 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ jobs:
sbt,
maven,
gradle,
scalacli,
mill,
feature,
cross,
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion project/V.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
@@ -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,
)
}
}
166 changes: 166 additions & 0 deletions tests/slow/src/main/scala/tests/scalacli/BaseScalaCliSuite.scala
Original file line number Diff line number Diff line change
@@ -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)
}
}
17 changes: 17 additions & 0 deletions tests/slow/src/main/scala/tests/scalacli/ScalaCliBuildLayout.scala
Original file line number Diff line number Diff line change
@@ -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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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") {

Expand Down Expand Up @@ -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,
"""|<<run>><<debug>>
|val x = 3
|
|<<run>><<debug>>
|@main def main() = {
| println("annotation")
|}""".stripMargin,
)
} yield ()
}
}
Original file line number Diff line number Diff line change
@@ -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(
Expand Down
Original file line number Diff line number Diff line change
@@ -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,
"""|<<run>><<debug>>
|val x = 3
|
|def main() = {
| println("annotation")
|}""".stripMargin,
)
} yield ()
}

}
Loading

0 comments on commit 99dc09f

Please sign in to comment.