Merge pull request #5878 from adpi2/bsp-test

[BSP] Add support for `buildTarget/test`
This commit is contained in:
eugene yokota 2020-09-21 14:23:11 -04:00 committed by GitHub
commit eaf74e462c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 790 additions and 17 deletions

View File

@ -399,9 +399,12 @@ object Keys {
val bspBuildTargetDependencySourcesItem = taskKey[DependencySourcesItem]("").withRank(DTask)
val bspBuildTargetCompile = inputKey[Unit]("").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 bspBuildTargetScalacOptions = inputKey[Unit]("").withRank(DTask)
val bspBuildTargetScalacOptionsItem = taskKey[ScalacOptionsItem]("").withRank(DTask)
val bspScalaTestClasses = inputKey[Unit]("Corresponds to buildTarget/scalaTestClasses request").withRank(DTask)
val bspScalaTestClassesItem = taskKey[ScalaTestClassesItem]("").withRank(DTask)
val bspScalaMainClasses = inputKey[Unit]("Corresponds to buildTarget/scalaMainClasses request").withRank(DTask)
val bspScalaMainClassesItem = taskKey[ScalaMainClassesItem]("").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.control.NonFatal
import sbt.Project._
import sbt.std.TaskExtra
object BuildServerProtocol {
import sbt.internal.bsp.codec.JsonProtocol._
private val capabilities = BuildServerCapabilities(
CompileProvider(BuildServerConnection.languages),
TestProvider(BuildServerConnection.languages),
RunProvider(BuildServerConnection.languages),
dependencySourcesProvider = true,
canReload = true
@ -130,6 +133,8 @@ object BuildServerProtocol {
}
}.evaluated,
bspBuildTargetCompile / aggregate := false,
bspBuildTargetTest := bspTestTask.evaluated,
bspBuildTargetTest / aggregate := false,
bspBuildTargetScalacOptions := Def.inputTaskDyn {
val s = state.value
val workspace = bspWorkspace.value
@ -142,6 +147,17 @@ object BuildServerProtocol {
}
}.evaluated,
bspBuildTargetScalacOptions / aggregate := false,
bspScalaTestClasses := Def.inputTaskDyn {
val s = state.value
val workspace = bspWorkspace.value
val targets = spaceDelimited().parsed.map(uri => BuildTargetIdentifier(URI.create(uri)))
val filter = ScopeFilter.in(targets.map(workspace))
Def.task {
val items = bspScalaTestClassesItem.all(filter).value
val result = ScalaTestClassesResult(items.toVector, None)
s.respondEvent(result)
}
}.evaluated,
bspScalaMainClasses := Def.inputTaskDyn {
val s = state.value
val workspace = bspWorkspace.value
@ -152,7 +168,8 @@ object BuildServerProtocol {
val result = ScalaMainClassesResult(items.toVector, None)
s.respondEvent(result)
}
}.evaluated
}.evaluated,
bspScalaMainClasses / aggregate := false
)
// This will be scoped to Compile, Test, IntegrationTest etc
@ -180,6 +197,7 @@ object BuildServerProtocol {
bspBuildTargetRun := bspRunTask.evaluated,
bspBuildTargetScalacOptionsItem := scalacOptionsTask.value,
bspInternalDependencyConfigurations := internalDependencyConfigurationsSetting.value,
bspScalaTestClassesItem := scalaTestClassesTask.value,
bspScalaMainClassesItem := scalaMainClassesTask.value
)
@ -242,6 +260,11 @@ object BuildServerProtocol {
val command = Keys.bspBuildTargetCompile.key
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" =>
val paramJson = json(r)
val param = Converter.fromJson[RunParams](json(r)).get
@ -267,6 +290,12 @@ object BuildServerProtocol {
val command = Keys.bspBuildTargetScalacOptions.key
val _ = callback.appendExec(s"$command $targets", Some(r.id))
case r: JsonRpcRequestMessage if r.method == "buildTarget/scalaTestClasses" =>
val param = Converter.fromJson[ScalaTestClassesParams](json(r)).get
val targets = param.targets.map(_.uri).mkString(" ")
val command = Keys.bspScalaTestClasses.key
val _ = callback.appendExec(s"$command $targets", Some(r.id))
case r: JsonRpcRequestMessage if r.method == "buildTarget/scalaMainClasses" =>
val param = Converter.fromJson[ScalaMainClassesParams](json(r)).get
val targets = param.targets.map(_.uri).mkString(" ")
@ -369,7 +398,7 @@ object BuildServerProtocol {
config <- configs
if dep != thisProjectRef || config.name != thisConfig.name
} 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)
Def.task {
BuildTarget(
@ -422,7 +451,6 @@ object BuildServerProtocol {
}
private def bspCompileTask: Def.Initialize[Task[Int]] = Def.task {
import sbt.Project._
Keys.compile.result.value match {
case Value(_) => StatusCode.Success
case Inc(_) =>
@ -477,6 +505,53 @@ object BuildServerProtocol {
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 {
val state = Keys.state.value
val logger = Keys.streams.value.log
@ -524,6 +599,17 @@ object BuildServerProtocol {
}
}
private def scalaTestClassesTask: Initialize[Task[ScalaTestClassesItem]] = Def.task {
val testClasses = Keys.definedTests.?.value
.getOrElse(Seq.empty)
.map(_.name)
.toVector
ScalaTestClassesItem(
bspTargetIdentifier.value,
testClasses
)
}
private def scalaMainClassesTask: Initialize[Task[ScalaMainClassesItem]] = Def.task {
val jvmOptions = Keys.javaOptions.value.toVector
val mainClasses = Keys.discoveredMainClasses.value.map(

View File

@ -6,12 +6,14 @@
package sbt.internal.bsp
/**
* @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
via method buildTarget/dependencySources
* @param canReload Reloading the workspace state through workspace/reload is supported
*/
final class BuildServerCapabilities private (
val compileProvider: Option[sbt.internal.bsp.CompileProvider],
val testProvider: Option[sbt.internal.bsp.TestProvider],
val runProvider: Option[sbt.internal.bsp.RunProvider],
val dependencySourcesProvider: Option[Boolean],
val canReload: Option[Boolean]) extends Serializable {
@ -19,17 +21,17 @@ final class BuildServerCapabilities private (
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
}
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 = {
"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 = {
new BuildServerCapabilities(compileProvider, runProvider, dependencySourcesProvider, canReload)
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, testProvider, runProvider, dependencySourcesProvider, canReload)
}
def withCompileProvider(compileProvider: Option[sbt.internal.bsp.CompileProvider]): BuildServerCapabilities = {
copy(compileProvider = compileProvider)
@ -37,6 +39,12 @@ final class BuildServerCapabilities private (
def withCompileProvider(compileProvider: sbt.internal.bsp.CompileProvider): BuildServerCapabilities = {
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 = {
copy(runProvider = runProvider)
}
@ -58,6 +66,6 @@ final class BuildServerCapabilities private (
}
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: 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: 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, 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

@ -7,7 +7,7 @@ package sbt.internal.bsp
/**
* Run Result
* @param originId An optional request id to know the origin of this report.
* @param statusCode A status code fore the execution.
* @param statusCode A status code for the execution.
*/
final class RunResult private (
val originId: Option[String],

View File

@ -5,6 +5,7 @@
// DO NOT EDIT MANUALLY
package sbt.internal.bsp
/**
* Scala Main Class Request
* The build target main classes request is sent from the client to the server
* to query for the list of main classes that can be fed as arguments to buildTarget/run.
* @param originId An optional number uniquely identifying a client request.

View File

@ -0,0 +1,40 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.bsp
/**
* @param target The build target that contains the test classes.
* @param classes The fully qualified names of the test classes in this target
*/
final class ScalaTestClassesItem private (
val target: sbt.internal.bsp.BuildTargetIdentifier,
val classes: Vector[String]) extends Serializable {
override def equals(o: Any): Boolean = o match {
case x: ScalaTestClassesItem => (this.target == x.target) && (this.classes == x.classes)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (37 * (17 + "sbt.internal.bsp.ScalaTestClassesItem".##) + target.##) + classes.##)
}
override def toString: String = {
"ScalaTestClassesItem(" + target + ", " + classes + ")"
}
private[this] def copy(target: sbt.internal.bsp.BuildTargetIdentifier = target, classes: Vector[String] = classes): ScalaTestClassesItem = {
new ScalaTestClassesItem(target, classes)
}
def withTarget(target: sbt.internal.bsp.BuildTargetIdentifier): ScalaTestClassesItem = {
copy(target = target)
}
def withClasses(classes: Vector[String]): ScalaTestClassesItem = {
copy(classes = classes)
}
}
object ScalaTestClassesItem {
def apply(target: sbt.internal.bsp.BuildTargetIdentifier, classes: Vector[String]): ScalaTestClassesItem = new ScalaTestClassesItem(target, classes)
}

View File

@ -0,0 +1,46 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.bsp
/**
* Scala Test Class Request
* 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.
* @param originId An optional number uniquely identifying a client request.
*/
final class ScalaTestClassesParams private (
val targets: Vector[sbt.internal.bsp.BuildTargetIdentifier],
val originId: Option[String]) extends Serializable {
override def equals(o: Any): Boolean = o match {
case x: ScalaTestClassesParams => (this.targets == x.targets) && (this.originId == x.originId)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (37 * (17 + "sbt.internal.bsp.ScalaTestClassesParams".##) + targets.##) + originId.##)
}
override def toString: String = {
"ScalaTestClassesParams(" + targets + ", " + originId + ")"
}
private[this] def copy(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier] = targets, originId: Option[String] = originId): ScalaTestClassesParams = {
new ScalaTestClassesParams(targets, originId)
}
def withTargets(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier]): ScalaTestClassesParams = {
copy(targets = targets)
}
def withOriginId(originId: Option[String]): ScalaTestClassesParams = {
copy(originId = originId)
}
def withOriginId(originId: String): ScalaTestClassesParams = {
copy(originId = Option(originId))
}
}
object ScalaTestClassesParams {
def apply(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier], originId: Option[String]): ScalaTestClassesParams = new ScalaTestClassesParams(targets, originId)
def apply(targets: Vector[sbt.internal.bsp.BuildTargetIdentifier], originId: String): ScalaTestClassesParams = new ScalaTestClassesParams(targets, Option(originId))
}

View File

@ -0,0 +1,41 @@
/**
* This code is generated using [[https://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.bsp
/** @param originId An optional id of the request that triggered this result. */
final class ScalaTestClassesResult private (
val items: Vector[sbt.internal.bsp.ScalaTestClassesItem],
val originId: Option[String]) extends Serializable {
override def equals(o: Any): Boolean = o match {
case x: ScalaTestClassesResult => (this.items == x.items) && (this.originId == x.originId)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (37 * (17 + "sbt.internal.bsp.ScalaTestClassesResult".##) + items.##) + originId.##)
}
override def toString: String = {
"ScalaTestClassesResult(" + items + ", " + originId + ")"
}
private[this] def copy(items: Vector[sbt.internal.bsp.ScalaTestClassesItem] = items, originId: Option[String] = originId): ScalaTestClassesResult = {
new ScalaTestClassesResult(items, originId)
}
def withItems(items: Vector[sbt.internal.bsp.ScalaTestClassesItem]): ScalaTestClassesResult = {
copy(items = items)
}
def withOriginId(originId: Option[String]): ScalaTestClassesResult = {
copy(originId = originId)
}
def withOriginId(originId: String): ScalaTestClassesResult = {
copy(originId = Option(originId))
}
}
object ScalaTestClassesResult {
def apply(items: Vector[sbt.internal.bsp.ScalaTestClassesItem], originId: Option[String]): ScalaTestClassesResult = new ScalaTestClassesResult(items, originId)
def apply(items: Vector[sbt.internal.bsp.ScalaTestClassesItem], originId: String): ScalaTestClassesResult = new ScalaTestClassesResult(items, Option(originId))
}

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
package sbt.internal.bsp.codec
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] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.BuildServerCapabilities = {
__jsOpt match {
case Some(__js) =>
unbuilder.beginObject(__js)
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 dependencySourcesProvider = unbuilder.readField[Option[Boolean]]("dependencySourcesProvider")
val canReload = unbuilder.readField[Option[Boolean]]("canReload")
unbuilder.endObject()
sbt.internal.bsp.BuildServerCapabilities(compileProvider, runProvider, dependencySourcesProvider, canReload)
sbt.internal.bsp.BuildServerCapabilities(compileProvider, testProvider, runProvider, dependencySourcesProvider, canReload)
case 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 = {
builder.beginObject()
builder.addField("compileProvider", obj.compileProvider)
builder.addField("testProvider", obj.testProvider)
builder.addField("runProvider", obj.runProvider)
builder.addField("dependencySourcesProvider", obj.dependencySourcesProvider)
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.InitializeBuildParamsFormats
with sbt.internal.bsp.codec.CompileProviderFormats
with sbt.internal.bsp.codec.TestProviderFormats
with sbt.internal.bsp.codec.RunProviderFormats
with sbt.internal.bsp.codec.BuildServerCapabilitiesFormats
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.CompileTaskFormats
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.RunResultFormats
with sbt.internal.bsp.codec.ScalaBuildTargetFormats
@ -44,6 +47,10 @@ trait JsonProtocol extends sjsonnew.BasicJsonProtocol
with sbt.internal.bsp.codec.ScalacOptionsResultFormats
with sbt.internal.bsp.codec.BspConnectionDetailsFormats
with sbt.internal.bsp.codec.MetalsMetadataFormats
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.ScalaMainClassesParamsFormats
with sbt.internal.bsp.codec.ScalaMainClassFormats
with sbt.internal.bsp.codec.ScalaMainClassesItemFormats

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 ScalaTestClassesItemFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sjsonnew.BasicJsonProtocol =>
implicit lazy val ScalaTestClassesItemFormat: JsonFormat[sbt.internal.bsp.ScalaTestClassesItem] = new JsonFormat[sbt.internal.bsp.ScalaTestClassesItem] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.ScalaTestClassesItem = {
__jsOpt match {
case Some(__js) =>
unbuilder.beginObject(__js)
val target = unbuilder.readField[sbt.internal.bsp.BuildTargetIdentifier]("target")
val classes = unbuilder.readField[Vector[String]]("classes")
unbuilder.endObject()
sbt.internal.bsp.ScalaTestClassesItem(target, classes)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.internal.bsp.ScalaTestClassesItem, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("target", obj.target)
builder.addField("classes", obj.classes)
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 ScalaTestClassesParamsFormats { self: sbt.internal.bsp.codec.BuildTargetIdentifierFormats with sjsonnew.BasicJsonProtocol =>
implicit lazy val ScalaTestClassesParamsFormat: JsonFormat[sbt.internal.bsp.ScalaTestClassesParams] = new JsonFormat[sbt.internal.bsp.ScalaTestClassesParams] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.ScalaTestClassesParams = {
__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")
unbuilder.endObject()
sbt.internal.bsp.ScalaTestClassesParams(targets, originId)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.internal.bsp.ScalaTestClassesParams, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("targets", obj.targets)
builder.addField("originId", obj.originId)
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 ScalaTestClassesResultFormats { self: sbt.internal.bsp.codec.ScalaTestClassesItemFormats with sjsonnew.BasicJsonProtocol =>
implicit lazy val ScalaTestClassesResultFormat: JsonFormat[sbt.internal.bsp.ScalaTestClassesResult] = new JsonFormat[sbt.internal.bsp.ScalaTestClassesResult] {
override def read[J](__jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.bsp.ScalaTestClassesResult = {
__jsOpt match {
case Some(__js) =>
unbuilder.beginObject(__js)
val items = unbuilder.readField[Vector[sbt.internal.bsp.ScalaTestClassesItem]]("items")
val originId = unbuilder.readField[Option[String]]("originId")
unbuilder.endObject()
sbt.internal.bsp.ScalaTestClassesResult(items, originId)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.internal.bsp.ScalaTestClassesResult, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("items", obj.items)
builder.addField("originId", obj.originId)
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 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.
compileProvider: sbt.internal.bsp.CompileProvider
# The languages the server supports test execution via method buildTarget/test
# testProvider: TestProvider
## The languages the server supports test execution via method buildTarget/test
testProvider: sbt.internal.bsp.TestProvider
# The languages the server supports run via method buildTarget/run
runProvider: sbt.internal.bsp.RunProvider
@ -198,6 +198,10 @@ type CompileProvider {
languageIds: [String]
}
type TestProvider {
languageIds: [String]
}
type RunProvider {
languageIds: [String]
}
@ -371,6 +375,43 @@ type CompileReport {
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
## The run request is sent from the client to the server to run a build target.
## The server communicates during the initialize handshake whether this method is supported or not.
@ -399,7 +440,7 @@ type RunResult {
## An optional request id to know the origin of this report.
originId: String
## A status code fore the execution.
## A status code for the execution.
statusCode: Int!
}
@ -507,6 +548,40 @@ type MetalsMetadata {
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
## 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.
type ScalaTestClassesParams {
targets: [sbt.internal.bsp.BuildTargetIdentifier]
## An optional number uniquely identifying a client request.
originId: String
}
type ScalaTestClassesResult {
items: [sbt.internal.bsp.ScalaTestClassesItem]
## An optional id of the request that triggered this result.
originId: String
}
type ScalaTestClassesItem {
## The build target that contains the test classes.
target: sbt.internal.bsp.BuildTargetIdentifier!
## The fully qualified names of the test classes in this target
classes: [String]
}
## Scala Main Class Request
## The build target main classes request is sent from the client to the server
## to query for the list of main classes that can be fed as arguments to buildTarget/run.
type ScalaMainClassesParams {

View File

@ -6,6 +6,9 @@ lazy val root = (project in file("."))
.aggregate(foo, util)
lazy val foo = project.in(file("foo"))
.settings(
libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.8" % "test",
)
.dependsOn(util)
lazy val util = project

View File

@ -1,5 +1,7 @@
package foo
object FooMain extends App {
println("Hello World!")
lazy val message = "Hello World!"
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

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

View File

@ -120,6 +120,57 @@ object BuildServerTest extends AbstractServerTest {
})
}
test("buildTarget/scalaTestClasses") { _ =>
val x = s"${svr.baseDirectory.getAbsoluteFile.toURI}#foo/Test"
svr.sendJsonRpc(
s"""{ "jsonrpc": "2.0", "id": "18", "method": "buildTarget/scalaTestClasses", "params": {
| "targets": [{ "uri": "$x" }]
|} }""".stripMargin
)
assert(svr.waitForString(10.seconds) { s =>
println(s)
(s contains """"id":"18"""") &&
(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""")
})
}
def initializeRequest(): Unit = {
svr.sendJsonRpc(
"""{ "jsonrpc": "2.0", "id": "10", "method": "build/initialize",