Add BSP buildTarget/test endpoint

This commit is contained in:
Adrien Piquerez 2020-09-21 12:16:31 +02:00
parent 50cf74cc67
commit f5753f763c
18 changed files with 483 additions and 17 deletions

View File

@ -398,6 +398,7 @@ object Keys {
val bspBuildTargetDependencySourcesItem = taskKey[DependencySourcesItem]("").withRank(DTask) val bspBuildTargetDependencySourcesItem = taskKey[DependencySourcesItem]("").withRank(DTask)
val bspBuildTargetCompile = inputKey[Unit]("").withRank(DTask) val bspBuildTargetCompile = inputKey[Unit]("").withRank(DTask)
val bspBuildTargetCompileItem = taskKey[Int]("").withRank(DTask) val bspBuildTargetCompileItem = taskKey[Int]("").withRank(DTask)
val bspBuildTargetTest = inputKey[Unit]("Corresponds to buildTarget/test request").withRank(DTask)
val bspBuildTargetRun = inputKey[Unit]("Corresponds to buildTarget/run request").withRank(DTask) val bspBuildTargetRun = inputKey[Unit]("Corresponds to buildTarget/run request").withRank(DTask)
val bspBuildTargetScalacOptions = inputKey[Unit]("").withRank(DTask) val bspBuildTargetScalacOptions = inputKey[Unit]("").withRank(DTask)
val bspBuildTargetScalacOptionsItem = taskKey[ScalacOptionsItem]("").withRank(DTask) val bspBuildTargetScalacOptionsItem = taskKey[ScalacOptionsItem]("").withRank(DTask)

View File

@ -30,12 +30,15 @@ import sjsonnew.support.scalajson.unsafe.{ CompactPrinter, Converter, Parser =>
import scala.util.{ Failure, Success, Try } import scala.util.{ Failure, Success, Try }
import scala.util.control.NonFatal import scala.util.control.NonFatal
import sbt.Project._
import sbt.std.TaskExtra
object BuildServerProtocol { object BuildServerProtocol {
import sbt.internal.bsp.codec.JsonProtocol._ import sbt.internal.bsp.codec.JsonProtocol._
private val capabilities = BuildServerCapabilities( private val capabilities = BuildServerCapabilities(
CompileProvider(BuildServerConnection.languages), CompileProvider(BuildServerConnection.languages),
TestProvider(BuildServerConnection.languages),
RunProvider(BuildServerConnection.languages), RunProvider(BuildServerConnection.languages),
dependencySourcesProvider = true, dependencySourcesProvider = true,
canReload = true canReload = true
@ -130,6 +133,8 @@ object BuildServerProtocol {
} }
}.evaluated, }.evaluated,
bspBuildTargetCompile / aggregate := false, bspBuildTargetCompile / aggregate := false,
bspBuildTargetTest := bspTestTask.evaluated,
bspBuildTargetTest / aggregate := false,
bspBuildTargetScalacOptions := Def.inputTaskDyn { bspBuildTargetScalacOptions := Def.inputTaskDyn {
val s = state.value val s = state.value
val workspace = bspWorkspace.value val workspace = bspWorkspace.value
@ -255,6 +260,11 @@ object BuildServerProtocol {
val command = Keys.bspBuildTargetCompile.key val command = Keys.bspBuildTargetCompile.key
val _ = callback.appendExec(s"$command $targets", Some(r.id)) val _ = callback.appendExec(s"$command $targets", Some(r.id))
case r: JsonRpcRequestMessage if r.method == "buildTarget/test" =>
val task = bspBuildTargetTest.key
val paramStr = CompactPrinter(json(r))
val _ = callback.appendExec(s"$task $paramStr", Some(r.id))
case r if r.method == "buildTarget/run" => case r if r.method == "buildTarget/run" =>
val paramJson = json(r) val paramJson = json(r)
val param = Converter.fromJson[RunParams](json(r)).get val param = Converter.fromJson[RunParams](json(r)).get
@ -388,7 +398,7 @@ object BuildServerProtocol {
config <- configs config <- configs
if dep != thisProjectRef || config.name != thisConfig.name if dep != thisProjectRef || config.name != thisConfig.name
} yield Keys.bspTargetIdentifier.in(dep, config) } yield Keys.bspTargetIdentifier.in(dep, config)
val capabilities = BuildTargetCapabilities(canCompile = true, canTest = false, canRun = false) val capabilities = BuildTargetCapabilities(canCompile = true, canTest = true, canRun = true)
val tags = BuildTargetTag.fromConfig(configuration.name) val tags = BuildTargetTag.fromConfig(configuration.name)
Def.task { Def.task {
BuildTarget( BuildTarget(
@ -441,7 +451,6 @@ object BuildServerProtocol {
} }
private def bspCompileTask: Def.Initialize[Task[Int]] = Def.task { private def bspCompileTask: Def.Initialize[Task[Int]] = Def.task {
import sbt.Project._
Keys.compile.result.value match { Keys.compile.result.value match {
case Value(_) => StatusCode.Success case Value(_) => StatusCode.Success
case Inc(_) => case Inc(_) =>
@ -496,6 +505,53 @@ object BuildServerProtocol {
runMainClassTask(mainClass, runParams.originId) runMainClassTask(mainClass, runParams.originId)
} }
private def bspTestTask: Def.Initialize[InputTask[Unit]] = Def.inputTaskDyn {
val testParams = jsonParser
.map(_.flatMap(json => Converter.fromJson[TestParams](json)))
.parsed
.get
val workspace = bspWorkspace.value
val resultTask: Def.Initialize[Task[Result[Seq[Unit]]]] = testParams.dataKind match {
case Some("scala-test") =>
val data = testParams.data.getOrElse(JNull)
val items = Converter.fromJson[ScalaTestParams](data) match {
case Failure(e) =>
throw LangServerError(ErrorCodes.ParseError, e.getMessage)
case Success(value) => value.testClasses
}
val testTasks: Seq[Def.Initialize[Task[Unit]]] = items.map { item =>
val scope = workspace(item.target)
item.classes.toList match {
case Nil => Def.task(())
case classes =>
(scope / testOnly).toTask(" " + classes.mkString(" "))
}
}
testTasks.joinWith(ts => TaskExtra.joinTasks(ts).join).result
case Some(dataKind) =>
throw LangServerError(
ErrorCodes.InvalidParams,
s"Unexpected data of kind '$dataKind', 'scala-main-class' is expected"
)
case None =>
// run allTests in testParams.targets
val filter = ScopeFilter.in(testParams.targets.map(workspace))
test.all(filter).result
}
Def.task {
val state = Keys.state.value
val statusCode = resultTask.value match {
case Value(_) => StatusCode.Success
case Inc(_) => StatusCode.Error
}
val _ = state.respondEvent(TestResult(testParams.originId, statusCode))
}
}
private def runMainClassTask(mainClass: ScalaMainClass, originId: Option[String]) = Def.task { private def runMainClassTask(mainClass: ScalaMainClass, originId: Option[String]) = Def.task {
val state = Keys.state.value val state = Keys.state.value
val logger = Keys.streams.value.log val logger = Keys.streams.value.log

View File

@ -6,12 +6,14 @@
package sbt.internal.bsp package sbt.internal.bsp
/** /**
* @param compileProvider The languages the server supports compilation via method buildTarget/compile. * @param compileProvider The languages the server supports compilation via method buildTarget/compile.
* @param testProvider The languages the server supports test execution via method buildTarget/test
* @param dependencySourcesProvider The server provides sources for library dependencies * @param dependencySourcesProvider The server provides sources for library dependencies
via method buildTarget/dependencySources via method buildTarget/dependencySources
* @param canReload Reloading the workspace state through workspace/reload is supported * @param canReload Reloading the workspace state through workspace/reload is supported
*/ */
final class BuildServerCapabilities private ( final class BuildServerCapabilities private (
val compileProvider: Option[sbt.internal.bsp.CompileProvider], val compileProvider: Option[sbt.internal.bsp.CompileProvider],
val testProvider: Option[sbt.internal.bsp.TestProvider],
val runProvider: Option[sbt.internal.bsp.RunProvider], val runProvider: Option[sbt.internal.bsp.RunProvider],
val dependencySourcesProvider: Option[Boolean], val dependencySourcesProvider: Option[Boolean],
val canReload: Option[Boolean]) extends Serializable { val canReload: Option[Boolean]) extends Serializable {
@ -19,17 +21,17 @@ final class BuildServerCapabilities private (
override def equals(o: Any): Boolean = o match { override def equals(o: Any): Boolean = o match {
case x: BuildServerCapabilities => (this.compileProvider == x.compileProvider) && (this.runProvider == x.runProvider) && (this.dependencySourcesProvider == x.dependencySourcesProvider) && (this.canReload == x.canReload) case x: BuildServerCapabilities => (this.compileProvider == x.compileProvider) && (this.testProvider == x.testProvider) && (this.runProvider == x.runProvider) && (this.dependencySourcesProvider == x.dependencySourcesProvider) && (this.canReload == x.canReload)
case _ => false case _ => false
} }
override def hashCode: Int = { override def hashCode: Int = {
37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.bsp.BuildServerCapabilities".##) + compileProvider.##) + runProvider.##) + dependencySourcesProvider.##) + canReload.##) 37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.bsp.BuildServerCapabilities".##) + compileProvider.##) + testProvider.##) + runProvider.##) + dependencySourcesProvider.##) + canReload.##)
} }
override def toString: String = { override def toString: String = {
"BuildServerCapabilities(" + compileProvider + ", " + runProvider + ", " + dependencySourcesProvider + ", " + canReload + ")" "BuildServerCapabilities(" + compileProvider + ", " + testProvider + ", " + runProvider + ", " + dependencySourcesProvider + ", " + canReload + ")"
} }
private[this] def copy(compileProvider: Option[sbt.internal.bsp.CompileProvider] = compileProvider, runProvider: Option[sbt.internal.bsp.RunProvider] = runProvider, dependencySourcesProvider: Option[Boolean] = dependencySourcesProvider, canReload: Option[Boolean] = canReload): BuildServerCapabilities = { private[this] def copy(compileProvider: Option[sbt.internal.bsp.CompileProvider] = compileProvider, testProvider: Option[sbt.internal.bsp.TestProvider] = testProvider, runProvider: Option[sbt.internal.bsp.RunProvider] = runProvider, dependencySourcesProvider: Option[Boolean] = dependencySourcesProvider, canReload: Option[Boolean] = canReload): BuildServerCapabilities = {
new BuildServerCapabilities(compileProvider, runProvider, dependencySourcesProvider, canReload) new BuildServerCapabilities(compileProvider, testProvider, runProvider, dependencySourcesProvider, canReload)
} }
def withCompileProvider(compileProvider: Option[sbt.internal.bsp.CompileProvider]): BuildServerCapabilities = { def withCompileProvider(compileProvider: Option[sbt.internal.bsp.CompileProvider]): BuildServerCapabilities = {
copy(compileProvider = compileProvider) copy(compileProvider = compileProvider)
@ -37,6 +39,12 @@ final class BuildServerCapabilities private (
def withCompileProvider(compileProvider: sbt.internal.bsp.CompileProvider): BuildServerCapabilities = { def withCompileProvider(compileProvider: sbt.internal.bsp.CompileProvider): BuildServerCapabilities = {
copy(compileProvider = Option(compileProvider)) copy(compileProvider = Option(compileProvider))
} }
def withTestProvider(testProvider: Option[sbt.internal.bsp.TestProvider]): BuildServerCapabilities = {
copy(testProvider = testProvider)
}
def withTestProvider(testProvider: sbt.internal.bsp.TestProvider): BuildServerCapabilities = {
copy(testProvider = Option(testProvider))
}
def withRunProvider(runProvider: Option[sbt.internal.bsp.RunProvider]): BuildServerCapabilities = { def withRunProvider(runProvider: Option[sbt.internal.bsp.RunProvider]): BuildServerCapabilities = {
copy(runProvider = runProvider) copy(runProvider = runProvider)
} }
@ -58,6 +66,6 @@ final class BuildServerCapabilities private (
} }
object BuildServerCapabilities { object BuildServerCapabilities {
def apply(compileProvider: Option[sbt.internal.bsp.CompileProvider], runProvider: Option[sbt.internal.bsp.RunProvider], dependencySourcesProvider: Option[Boolean], canReload: Option[Boolean]): BuildServerCapabilities = new BuildServerCapabilities(compileProvider, runProvider, dependencySourcesProvider, canReload) def apply(compileProvider: Option[sbt.internal.bsp.CompileProvider], testProvider: Option[sbt.internal.bsp.TestProvider], runProvider: Option[sbt.internal.bsp.RunProvider], dependencySourcesProvider: Option[Boolean], canReload: Option[Boolean]): BuildServerCapabilities = new BuildServerCapabilities(compileProvider, testProvider, runProvider, dependencySourcesProvider, canReload)
def apply(compileProvider: sbt.internal.bsp.CompileProvider, runProvider: sbt.internal.bsp.RunProvider, dependencySourcesProvider: Boolean, canReload: Boolean): BuildServerCapabilities = new BuildServerCapabilities(Option(compileProvider), Option(runProvider), Option(dependencySourcesProvider), Option(canReload)) def apply(compileProvider: sbt.internal.bsp.CompileProvider, testProvider: sbt.internal.bsp.TestProvider, runProvider: sbt.internal.bsp.RunProvider, dependencySourcesProvider: Boolean, canReload: Boolean): BuildServerCapabilities = new BuildServerCapabilities(Option(compileProvider), Option(testProvider), Option(runProvider), Option(dependencySourcesProvider), Option(canReload))
} }

View File

@ -0,0 +1,38 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.bsp
/**
* Scala Test Params
* ScalaTestParams contains scala-specific metadata for testing Scala targets.
* This metadata is embedded in the data field of the buildTarget/test request
* when the dataKind field contains "scala-test".
*/
final class ScalaTestParams private (
val testClasses: Vector[sbt.internal.bsp.ScalaTestClassesItem]) extends Serializable {
override def equals(o: Any): Boolean = o match {
case x: ScalaTestParams => (this.testClasses == x.testClasses)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (17 + "sbt.internal.bsp.ScalaTestParams".##) + testClasses.##)
}
override def toString: String = {
"ScalaTestParams(" + testClasses + ")"
}
private[this] def copy(testClasses: Vector[sbt.internal.bsp.ScalaTestClassesItem] = testClasses): ScalaTestParams = {
new ScalaTestParams(testClasses)
}
def withTestClasses(testClasses: Vector[sbt.internal.bsp.ScalaTestClassesItem]): ScalaTestParams = {
copy(testClasses = testClasses)
}
}
object ScalaTestParams {
def apply(testClasses: Vector[sbt.internal.bsp.ScalaTestClassesItem]): ScalaTestParams = new ScalaTestParams(testClasses)
}

View File

@ -0,0 +1,70 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.bsp
/**
* Test Request
* The test build target request is sent from the client to the server to test the given list of build targets.
* The server communicates during the initialize handshake whether this method is supported or not.
* @param targets A sequence of build targets to test.
* @param originId An option identifier generated by the client to identify this request.
The server may include this id in triggered notifications or responses.
* @param arguments Optional arguments to the test execution engine.
* @param dataKind Kind of data to expect in the `data` field.
If this field is not set, the kind of data is not specified.
* @param data Language-specific metadata for this test execution.
*/
final class TestParams private (
val targets: Vector[sbt.internal.bsp.BuildTargetIdentifier],
val originId: Option[String],
val arguments: Vector[String],
val dataKind: Option[String],
val data: Option[sjsonnew.shaded.scalajson.ast.unsafe.JValue]) extends Serializable {
override def equals(o: Any): Boolean = o match {
case x: TestParams => (this.targets == x.targets) && (this.originId == x.originId) && (this.arguments == x.arguments) && (this.dataKind == x.dataKind) && (this.data == x.data)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (37 * (37 * (37 * (37 * (17 + "sbt.internal.bsp.TestParams".##) + targets.##) + originId.##) + arguments.##) + dataKind.##) + data.##)
}
override def toString: String = {
"TestParams(" + targets + ", " + originId + ", " + arguments + ", " + dataKind + ", " + data + ")"
}
private[this] def copy(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier] = targets, originId: Option[String] = originId, arguments: Vector[String] = arguments, dataKind: Option[String] = dataKind, data: Option[sjsonnew.shaded.scalajson.ast.unsafe.JValue] = data): TestParams = {
new TestParams(targets, originId, arguments, dataKind, data)
}
def withTargets(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier]): TestParams = {
copy(targets = targets)
}
def withOriginId(originId: Option[String]): TestParams = {
copy(originId = originId)
}
def withOriginId(originId: String): TestParams = {
copy(originId = Option(originId))
}
def withArguments(arguments: Vector[String]): TestParams = {
copy(arguments = arguments)
}
def withDataKind(dataKind: Option[String]): TestParams = {
copy(dataKind = dataKind)
}
def withDataKind(dataKind: String): TestParams = {
copy(dataKind = Option(dataKind))
}
def withData(data: Option[sjsonnew.shaded.scalajson.ast.unsafe.JValue]): TestParams = {
copy(data = data)
}
def withData(data: sjsonnew.shaded.scalajson.ast.unsafe.JValue): TestParams = {
copy(data = Option(data))
}
}
object TestParams {
def apply(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier], originId: Option[String], arguments: Vector[String], dataKind: Option[String], data: Option[sjsonnew.shaded.scalajson.ast.unsafe.JValue]): TestParams = new TestParams(targets, originId, arguments, dataKind, data)
def apply(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier], originId: String, arguments: Vector[String], dataKind: String, data: sjsonnew.shaded.scalajson.ast.unsafe.JValue): TestParams = new TestParams(targets, Option(originId), arguments, Option(dataKind), Option(data))
}

View File

@ -0,0 +1,32 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.bsp
final class TestProvider private (
val languageIds: Vector[String]) extends Serializable {
override def equals(o: Any): Boolean = o match {
case x: TestProvider => (this.languageIds == x.languageIds)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (17 + "sbt.internal.bsp.TestProvider".##) + languageIds.##)
}
override def toString: String = {
"TestProvider(" + languageIds + ")"
}
private[this] def copy(languageIds: Vector[String] = languageIds): TestProvider = {
new TestProvider(languageIds)
}
def withLanguageIds(languageIds: Vector[String]): TestProvider = {
copy(languageIds = languageIds)
}
}
object TestProvider {
def apply(languageIds: Vector[String]): TestProvider = new TestProvider(languageIds)
}

View File

@ -0,0 +1,45 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.bsp
/**
* Test Result
* @param originId An optional request id to know the origin of this report.
* @param statusCode A status code for the execution.
*/
final class TestResult private (
val originId: Option[String],
val statusCode: Int) extends Serializable {
override def equals(o: Any): Boolean = o match {
case x: TestResult => (this.originId == x.originId) && (this.statusCode == x.statusCode)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (37 * (17 + "sbt.internal.bsp.TestResult".##) + originId.##) + statusCode.##)
}
override def toString: String = {
"TestResult(" + originId + ", " + statusCode + ")"
}
private[this] def copy(originId: Option[String] = originId, statusCode: Int = statusCode): TestResult = {
new TestResult(originId, statusCode)
}
def withOriginId(originId: Option[String]): TestResult = {
copy(originId = originId)
}
def withOriginId(originId: String): TestResult = {
copy(originId = Option(originId))
}
def withStatusCode(statusCode: Int): TestResult = {
copy(statusCode = statusCode)
}
}
object TestResult {
def apply(originId: Option[String], statusCode: Int): TestResult = new TestResult(originId, statusCode)
def apply(originId: String, statusCode: Int): TestResult = new TestResult(Option(originId), statusCode)
}

View File

@ -5,18 +5,19 @@
// DO NOT EDIT MANUALLY // DO NOT EDIT MANUALLY
package sbt.internal.bsp.codec package sbt.internal.bsp.codec
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError } import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
trait BuildServerCapabilitiesFormats { self: sbt.internal.bsp.codec.CompileProviderFormats with sbt.internal.bsp.codec.RunProviderFormats with sjsonnew.BasicJsonProtocol => trait BuildServerCapabilitiesFormats { self: sbt.internal.bsp.codec.CompileProviderFormats with sbt.internal.bsp.codec.TestProviderFormats with sbt.internal.bsp.codec.RunProviderFormats with sjsonnew.BasicJsonProtocol =>
implicit lazy val BuildServerCapabilitiesFormat: JsonFormat[sbt.internal.bsp.BuildServerCapabilities] = new JsonFormat[sbt.internal.bsp.BuildServerCapabilities] { implicit lazy val BuildServerCapabilitiesFormat: JsonFormat[sbt.internal.bsp.BuildServerCapabilities] = new JsonFormat[sbt.internal.bsp.BuildServerCapabilities] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.BuildServerCapabilities = { override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.BuildServerCapabilities = {
__jsOpt match { __jsOpt match {
case Some(__js) => case Some(__js) =>
unbuilder.beginObject(__js) unbuilder.beginObject(__js)
val compileProvider = unbuilder.readField[Option[sbt.internal.bsp.CompileProvider]]("compileProvider") val compileProvider = unbuilder.readField[Option[sbt.internal.bsp.CompileProvider]]("compileProvider")
val testProvider = unbuilder.readField[Option[sbt.internal.bsp.TestProvider]]("testProvider")
val runProvider = unbuilder.readField[Option[sbt.internal.bsp.RunProvider]]("runProvider") val runProvider = unbuilder.readField[Option[sbt.internal.bsp.RunProvider]]("runProvider")
val dependencySourcesProvider = unbuilder.readField[Option[Boolean]]("dependencySourcesProvider") val dependencySourcesProvider = unbuilder.readField[Option[Boolean]]("dependencySourcesProvider")
val canReload = unbuilder.readField[Option[Boolean]]("canReload") val canReload = unbuilder.readField[Option[Boolean]]("canReload")
unbuilder.endObject() unbuilder.endObject()
sbt.internal.bsp.BuildServerCapabilities(compileProvider, runProvider, dependencySourcesProvider, canReload) sbt.internal.bsp.BuildServerCapabilities(compileProvider, testProvider, runProvider, dependencySourcesProvider, canReload)
case None => case None =>
deserializationError("Expected JsObject but found None") deserializationError("Expected JsObject but found None")
} }
@ -24,6 +25,7 @@ implicit lazy val BuildServerCapabilitiesFormat: JsonFormat[sbt.internal.bsp.Bui
override def write[J](obj: sbt.internal.bsp.BuildServerCapabilities, builder: Builder[J]): Unit = { override def write[J](obj: sbt.internal.bsp.BuildServerCapabilities, builder: Builder[J]): Unit = {
builder.beginObject() builder.beginObject()
builder.addField("compileProvider", obj.compileProvider) builder.addField("compileProvider", obj.compileProvider)
builder.addField("testProvider", obj.testProvider)
builder.addField("runProvider", obj.runProvider) builder.addField("runProvider", obj.runProvider)
builder.addField("dependencySourcesProvider", obj.dependencySourcesProvider) builder.addField("dependencySourcesProvider", obj.dependencySourcesProvider)
builder.addField("canReload", obj.canReload) builder.addField("canReload", obj.canReload)

View File

@ -17,6 +17,7 @@ trait JsonProtocol extends sjsonnew.BasicJsonProtocol
with sbt.internal.bsp.codec.BuildClientCapabilitiesFormats with sbt.internal.bsp.codec.BuildClientCapabilitiesFormats
with sbt.internal.bsp.codec.InitializeBuildParamsFormats with sbt.internal.bsp.codec.InitializeBuildParamsFormats
with sbt.internal.bsp.codec.CompileProviderFormats with sbt.internal.bsp.codec.CompileProviderFormats
with sbt.internal.bsp.codec.TestProviderFormats
with sbt.internal.bsp.codec.RunProviderFormats with sbt.internal.bsp.codec.RunProviderFormats
with sbt.internal.bsp.codec.BuildServerCapabilitiesFormats with sbt.internal.bsp.codec.BuildServerCapabilitiesFormats
with sbt.internal.bsp.codec.InitializeBuildResultFormats with sbt.internal.bsp.codec.InitializeBuildResultFormats
@ -35,6 +36,8 @@ trait JsonProtocol extends sjsonnew.BasicJsonProtocol
with sbt.internal.bsp.codec.BspCompileResultFormats with sbt.internal.bsp.codec.BspCompileResultFormats
with sbt.internal.bsp.codec.CompileTaskFormats with sbt.internal.bsp.codec.CompileTaskFormats
with sbt.internal.bsp.codec.CompileReportFormats with sbt.internal.bsp.codec.CompileReportFormats
with sbt.internal.bsp.codec.TestParamsFormats
with sbt.internal.bsp.codec.TestResultFormats
with sbt.internal.bsp.codec.RunParamsFormats with sbt.internal.bsp.codec.RunParamsFormats
with sbt.internal.bsp.codec.RunResultFormats with sbt.internal.bsp.codec.RunResultFormats
with sbt.internal.bsp.codec.ScalaBuildTargetFormats with sbt.internal.bsp.codec.ScalaBuildTargetFormats
@ -44,8 +47,9 @@ trait JsonProtocol extends sjsonnew.BasicJsonProtocol
with sbt.internal.bsp.codec.ScalacOptionsResultFormats with sbt.internal.bsp.codec.ScalacOptionsResultFormats
with sbt.internal.bsp.codec.BspConnectionDetailsFormats with sbt.internal.bsp.codec.BspConnectionDetailsFormats
with sbt.internal.bsp.codec.MetalsMetadataFormats with sbt.internal.bsp.codec.MetalsMetadataFormats
with sbt.internal.bsp.codec.ScalaTestClassesParamsFormats
with sbt.internal.bsp.codec.ScalaTestClassesItemFormats with sbt.internal.bsp.codec.ScalaTestClassesItemFormats
with sbt.internal.bsp.codec.ScalaTestParamsFormats
with sbt.internal.bsp.codec.ScalaTestClassesParamsFormats
with sbt.internal.bsp.codec.ScalaTestClassesResultFormats with sbt.internal.bsp.codec.ScalaTestClassesResultFormats
with sbt.internal.bsp.codec.ScalaMainClassesParamsFormats with sbt.internal.bsp.codec.ScalaMainClassesParamsFormats
with sbt.internal.bsp.codec.ScalaMainClassFormats with sbt.internal.bsp.codec.ScalaMainClassFormats

View File

@ -0,0 +1,27 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.bsp.codec
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
trait ScalaTestParamsFormats { self: sbt.internal.bsp.codec.ScalaTestClassesItemFormats with sjsonnew.BasicJsonProtocol =>
implicit lazy val ScalaTestParamsFormat: JsonFormat[sbt.internal.bsp.ScalaTestParams] = new JsonFormat[sbt.internal.bsp.ScalaTestParams] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.ScalaTestParams = {
__jsOpt match {
case Some(__js) =>
unbuilder.beginObject(__js)
val testClasses = unbuilder.readField[Vector[sbt.internal.bsp.ScalaTestClassesItem]]("testClasses")
unbuilder.endObject()
sbt.internal.bsp.ScalaTestParams(testClasses)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.internal.bsp.ScalaTestParams, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("testClasses", obj.testClasses)
builder.endObject()
}
}
}

View File

@ -0,0 +1,35 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.bsp.codec
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
trait TestParamsFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sbt.internal.util.codec.JValueFormats with sjsonnew.BasicJsonProtocol =>
implicit lazy val TestParamsFormat: JsonFormat[sbt.internal.bsp.TestParams] = new JsonFormat[sbt.internal.bsp.TestParams] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.TestParams = {
__jsOpt match {
case Some(__js) =>
unbuilder.beginObject(__js)
val targets = unbuilder.readField[Vector[sbt.internal.bsp.BuildTargetIdentifier]]("targets")
val originId = unbuilder.readField[Option[String]]("originId")
val arguments = unbuilder.readField[Vector[String]]("arguments")
val dataKind = unbuilder.readField[Option[String]]("dataKind")
val data = unbuilder.readField[Option[sjsonnew.shaded.scalajson.ast.unsafe.JValue]]("data")
unbuilder.endObject()
sbt.internal.bsp.TestParams(targets, originId, arguments, dataKind, data)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.internal.bsp.TestParams, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("targets", obj.targets)
builder.addField("originId", obj.originId)
builder.addField("arguments", obj.arguments)
builder.addField("dataKind", obj.dataKind)
builder.addField("data", obj.data)
builder.endObject()
}
}
}

View File

@ -0,0 +1,27 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.bsp.codec
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
trait TestProviderFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val TestProviderFormat: JsonFormat[sbt.internal.bsp.TestProvider] = new JsonFormat[sbt.internal.bsp.TestProvider] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.TestProvider = {
__jsOpt match {
case Some(__js) =>
unbuilder.beginObject(__js)
val languageIds = unbuilder.readField[Vector[String]]("languageIds")
unbuilder.endObject()
sbt.internal.bsp.TestProvider(languageIds)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.internal.bsp.TestProvider, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("languageIds", obj.languageIds)
builder.endObject()
}
}
}

View File

@ -0,0 +1,29 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.bsp.codec
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
trait TestResultFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val TestResultFormat: JsonFormat[sbt.internal.bsp.TestResult] = new JsonFormat[sbt.internal.bsp.TestResult] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.TestResult = {
__jsOpt match {
case Some(__js) =>
unbuilder.beginObject(__js)
val originId = unbuilder.readField[Option[String]]("originId")
val statusCode = unbuilder.readField[Int]("statusCode")
unbuilder.endObject()
sbt.internal.bsp.TestResult(originId, statusCode)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.internal.bsp.TestResult, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("originId", obj.originId)
builder.addField("statusCode", obj.statusCode)
builder.endObject()
}
}
}

View File

@ -168,8 +168,8 @@ type BuildServerCapabilities {
## The languages the server supports compilation via method buildTarget/compile. ## The languages the server supports compilation via method buildTarget/compile.
compileProvider: sbt.internal.bsp.CompileProvider compileProvider: sbt.internal.bsp.CompileProvider
# The languages the server supports test execution via method buildTarget/test ## The languages the server supports test execution via method buildTarget/test
# testProvider: TestProvider testProvider: sbt.internal.bsp.TestProvider
# The languages the server supports run via method buildTarget/run # The languages the server supports run via method buildTarget/run
runProvider: sbt.internal.bsp.RunProvider runProvider: sbt.internal.bsp.RunProvider
@ -198,6 +198,10 @@ type CompileProvider {
languageIds: [String] languageIds: [String]
} }
type TestProvider {
languageIds: [String]
}
type RunProvider { type RunProvider {
languageIds: [String] languageIds: [String]
} }
@ -371,8 +375,42 @@ type CompileReport {
time: Int time: Int
} }
## Test Request
## The test build target request is sent from the client to the server to test the given list of build targets.
## The server communicates during the initialize handshake whether this method is supported or not.
type TestParams {
## A sequence of build targets to test.
targets: [sbt.internal.bsp.BuildTargetIdentifier]
## An option identifier generated by the client to identify this request.
## The server may include this id in triggered notifications or responses.
originId: String
## Optional arguments to the test execution engine.
arguments: [String]
## Kind of data to expect in the `data` field.
## If this field is not set, the kind of data is not specified.
dataKind: String
## Language-specific metadata for this test execution.
data: sjsonnew.shaded.scalajson.ast.unsafe.JValue
}
## Test Result
type TestResult {
## An optional request id to know the origin of this report.
originId: String
## A status code for the execution.
statusCode: Int!
# Kind of data to expect in the `data` field.
# If this field is not set, the kind of data is not specified.
# dataKind: String
# data: sjsonnew.shaded.scalajson.ast.unsafe.JValue
}
## Run Request ## Run Request
## The run request is sent from the client to the server to run a build target. ## The run request is sent from the client to the server to run a build target.
@ -510,6 +548,14 @@ type MetalsMetadata {
supportedScalaVersions: [String] supportedScalaVersions: [String]
} }
## Scala Test Params
## ScalaTestParams contains scala-specific metadata for testing Scala targets.
## This metadata is embedded in the data field of the buildTarget/test request
## when the dataKind field contains "scala-test".
type ScalaTestParams {
testClasses: [sbt.internal.bsp.ScalaTestClassesItem]
}
## Scala Test Class Request ## Scala Test Class Request
## The build target scala test options request is sent from the client to the server ## The build target scala test options request is sent from the client to the server
## to query for the list of fully qualified names of test clases in a given list of targets. ## to query for the list of fully qualified names of test clases in a given list of targets.

View File

@ -1,7 +1,7 @@
package foo package foo
object FooMain extends App { object FooMain extends App {
val message = "Hello World!" lazy val message = "Hello World!"
println(message) println(message)
} }

View File

@ -0,0 +1,9 @@
package foo
import org.scalatest.FreeSpec
class FailingTest extends FreeSpec {
"it should fail" in {
throw new Exception("Test failed")
}
}

View File

@ -2,7 +2,7 @@ package foo
import org.scalatest.FreeSpec import org.scalatest.FreeSpec
class FooTest extends FreeSpec { class FooTest extends FreeSpec {
"test message" in { "test message" in {
assert(FooMain.message == "Hello World!") assert(FooMain.message == "Hello World!")
} }

View File

@ -130,7 +130,44 @@ object BuildServerTest extends AbstractServerTest {
assert(svr.waitForString(10.seconds) { s => assert(svr.waitForString(10.seconds) { s =>
println(s) println(s)
(s contains """"id":"18"""") && (s contains """"id":"18"""") &&
(s contains """"classes":["foo.FooTest"]""") (s contains """"classes":["foo.FailingTest","foo.FooTest"]""")
})
}
test("buildTarget/test: run all tests") { _ =>
val x = s"${svr.baseDirectory.getAbsoluteFile.toURI}#foo/Test"
svr.sendJsonRpc(
s"""{ "jsonrpc": "2.0", "id": "19", "method": "buildTarget/test", "params": {
| "targets": [{ "uri": "$x" }]
|} }""".stripMargin
)
assert(svr.waitForString(10.seconds) { s =>
println(s)
(s contains """"id":"19"""") &&
(s contains """"statusCode":2""")
})
}
test("buildTarget/test: run one test class") { _ =>
val x = s"${svr.baseDirectory.getAbsoluteFile.toURI}#foo/Test"
svr.sendJsonRpc(
s"""{ "jsonrpc": "2.0", "id": "20", "method": "buildTarget/test", "params": {
| "targets": [{ "uri": "$x" }],
| "dataKind": "scala-test",
| "data": {
| "testClasses": [
| {
| "target": { "uri": "$x" },
| "classes": ["foo.FooTest"]
| }
| ]
| }
|} }""".stripMargin
)
assert(svr.waitForString(10.seconds) { s =>
println(s)
(s contains """"id":"20"""") &&
(s contains """"statusCode":1""")
}) })
} }