sbt/launcher-package/integration-test/src/test/scala/BspConfigTest.scala

91 lines
3.3 KiB
Scala

package example.test
import scala.sys.process.*
import java.io.File
import java.util.Locale
import sbt.io.IO
import verify.BasicTestSuite
// Test for issues #7792/#7794: BSP config generation and argv execution
object BspConfigTest extends BasicTestSuite:
lazy val isWindows: Boolean =
sys.props("os.name").toLowerCase(Locale.ENGLISH).contains("windows")
lazy val sbtScript = IntegrationTestPaths.sbtScript(isWindows)
private def launcherCmd = LauncherTestHelper.launcherCommand(sbtScript.getAbsolutePath)
def sbtProcessInDir(dir: File)(args: String*) =
Process(
launcherCmd ++ args,
dir,
"JAVA_OPTS" -> "",
"SBT_OPTS" -> ""
)
test("sbt bspConfig") {
import ujson.*
IO.withTemporaryDirectory { tmp =>
// Create minimal build.sbt for the test project
IO.write(new File(tmp, "build.sbt"), """name := "test-bsp-config"""")
// Run bspConfig to generate .bsp/sbt.json
val configResult = sbtProcessInDir(tmp)("bspConfig", "--batch").!
assert(configResult == 0, s"bspConfig command failed with exit code $configResult")
// Verify .bsp/sbt.json exists
val bspFile = new File(tmp, ".bsp/sbt.json")
assert(bspFile.exists, ".bsp/sbt.json should exist after running bspConfig")
// Parse and verify JSON content
val content = IO.read(bspFile)
val json = ujson.read(content)
// Extract argv array from JSON
val argvValue = json.obj.get("argv")
assert(argvValue.isDefined, "argv field not found in sbt.json")
val argv = argvValue.get.arr.map(_.str).toVector
// Verify argv structure
assert(argv.nonEmpty, "argv should not be empty")
// When sbt script is available, argv uses the sbt script with "bsp" command.
// When not, argv falls back to direct java invocation with "-bsp" flag.
val usesSbtScript = argv.last == "bsp" && !argv.head.contains("java")
val usesJavaDirect = argv.head.contains("java") && argv.contains("-bsp")
assert(
usesSbtScript || usesJavaDirect,
s"argv should either use sbt script with 'bsp' command or java with '-bsp' flag, got: $argv"
)
// Test execution of the generated argv
// Run the BSP command with a very short timeout to verify it starts correctly
// We just need to verify the command doesn't fail immediately on startup
if (!isWindows) {
// On Unix, we can test the argv execution
// Create a process and check if it starts (will timeout waiting for BSP input)
val process = Process(argv.toSeq, tmp)
val processBuilder = process.run(ProcessLogger(_ => (), _ => ()))
// Give it a moment to fail if it's going to fail immediately
Thread.sleep(500)
// If still running, it means the BSP server started successfully
val isAlive = processBuilder.isAlive()
processBuilder.destroy()
// The process should either still be alive (waiting for BSP messages)
// or have exited with code 0 (graceful)
if (!isAlive) {
val exitCode = processBuilder.exitValue()
assert(
exitCode == 0 || exitCode == 143, // 143 = SIGTERM from destroy()
s"BSP process failed with exit code $exitCode"
)
}
}
}
()
}
end BspConfigTest