Add BSP `buildTarget/scalaTestClasses` endpoint

This commit is contained in:
Adrien Piquerez 2020-09-17 15:58:14 +02:00
parent cfaede2bf6
commit 50cf74cc67
16 changed files with 311 additions and 4 deletions

View File

@ -401,6 +401,8 @@ object Keys {
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

@ -142,6 +142,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 +163,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 +192,7 @@ object BuildServerProtocol {
bspBuildTargetRun := bspRunTask.evaluated,
bspBuildTargetScalacOptionsItem := scalacOptionsTask.value,
bspInternalDependencyConfigurations := internalDependencyConfigurationsSetting.value,
bspScalaTestClassesItem := scalaTestClassesTask.value,
bspScalaMainClassesItem := scalaMainClassesTask.value
)
@ -267,6 +280,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(" ")
@ -524,6 +543,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

@ -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

@ -44,6 +44,9 @@ 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.ScalaTestClassesParamsFormats
with sbt.internal.bsp.codec.ScalaTestClassesItemFormats
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

@ -371,6 +371,9 @@ type CompileReport {
time: Int
}
## 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 +402,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 +510,32 @@ type MetalsMetadata {
supportedScalaVersions: [String]
}
## 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!")
val message = "Hello World!"
println(message)
}

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,20 @@ 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.FooTest"]""")
})
}
def initializeRequest(): Unit = {
svr.sendJsonRpc(
"""{ "jsonrpc": "2.0", "id": "10", "method": "build/initialize",