From 23b50688ba7b50861544bc09a07a2f278f23ac19 Mon Sep 17 00:00:00 2001 From: Kamil Podsiadlo Date: Fri, 4 Mar 2022 10:53:25 +0100 Subject: [PATCH 1/3] feat: add optional framework field to the bsp --- main/src/main/scala/sbt/Keys.scala | 2 +- .../internal/server/BuildServerProtocol.scala | 35 +++++++++++++------ .../internal/bsp/ScalaTestClassesItem.scala | 23 ++++++++---- .../codec/ScalaTestClassesItemFormats.scala | 4 ++- protocol/src/main/contraband/bsp.contra | 3 ++ 5 files changed, 48 insertions(+), 19 deletions(-) diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index 1a248b1df..2270ee573 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -419,7 +419,7 @@ object Keys { 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 bspScalaTestClassesItem = taskKey[Seq[ScalaTestClassesItem]]("").withRank(DTask) val bspScalaMainClasses = inputKey[Unit]("Corresponds to buildTarget/scalaMainClasses request").withRank(DTask) val bspScalaMainClassesItem = taskKey[ScalaMainClassesItem]("").withRank(DTask) val bspReporter = taskKey[BuildServerReporter]("").withRank(DTask) diff --git a/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala b/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala index b5006aabb..d289884bf 100644 --- a/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala +++ b/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala @@ -242,7 +242,7 @@ object BuildServerProtocol { val filter = ScopeFilter.in(workspace.scopes.values.toList) Def.task { val items = bspScalaTestClassesItem.result.all(filter).value - val successfulItems = anyOrThrow(items) + val successfulItems = anyOrThrow(items).flatten.toVector val result = ScalaTestClassesResult(successfulItems.toVector, None) s.respondEvent(result) } @@ -840,15 +840,30 @@ 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 scalaTestClassesTask: Initialize[Task[Seq[ScalaTestClassesItem]]] = Def.task { + Keys.definedTests.?.value match { + case None => Vector.empty + case Some(definitions) => + val fingerprints = Keys.loadedTestFrameworks.?.value + .getOrElse(Map.empty) + .values + .flatMap { framework => + framework.fingerprints().map(fingerprint => (fingerprint, framework)) + } + .toMap + + definitions + .groupBy(defn => fingerprints.get(defn.fingerprint)) + .map { + case (framework, definitions) => + ScalaTestClassesItem( + bspTargetIdentifier.value, + definitions.map(_.name).toVector, + framework.map(_.name()) + ) + } + .toSeq + } } private def scalaMainClassesTask: Initialize[Task[ScalaMainClassesItem]] = Def.task { diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/ScalaTestClassesItem.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/ScalaTestClassesItem.scala index 210e8cc30..e274fda16 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/ScalaTestClassesItem.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/ScalaTestClassesItem.scala @@ -7,25 +7,27 @@ 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 + * @param framework The name of the test framework used in test classes. */ final class ScalaTestClassesItem private ( val target: sbt.internal.bsp.BuildTargetIdentifier, - val classes: Vector[String]) extends Serializable { + val classes: Vector[String], + val framework: Option[String]) extends Serializable { override def equals(o: Any): Boolean = this.eq(o.asInstanceOf[AnyRef]) || (o match { - case x: ScalaTestClassesItem => (this.target == x.target) && (this.classes == x.classes) + case x: ScalaTestClassesItem => (this.target == x.target) && (this.classes == x.classes) && (this.framework == x.framework) case _ => false }) override def hashCode: Int = { - 37 * (37 * (37 * (17 + "sbt.internal.bsp.ScalaTestClassesItem".##) + target.##) + classes.##) + 37 * (37 * (37 * (37 * (17 + "sbt.internal.bsp.ScalaTestClassesItem".##) + target.##) + classes.##) + framework.##) } override def toString: String = { - "ScalaTestClassesItem(" + target + ", " + classes + ")" + "ScalaTestClassesItem(" + target + ", " + classes + ", " + framework + ")" } - private[this] def copy(target: sbt.internal.bsp.BuildTargetIdentifier = target, classes: Vector[String] = classes): ScalaTestClassesItem = { - new ScalaTestClassesItem(target, classes) + private[this] def copy(target: sbt.internal.bsp.BuildTargetIdentifier = target, classes: Vector[String] = classes, framework: Option[String] = framework): ScalaTestClassesItem = { + new ScalaTestClassesItem(target, classes, framework) } def withTarget(target: sbt.internal.bsp.BuildTargetIdentifier): ScalaTestClassesItem = { copy(target = target) @@ -33,8 +35,15 @@ final class ScalaTestClassesItem private ( def withClasses(classes: Vector[String]): ScalaTestClassesItem = { copy(classes = classes) } + def withFramework(framework: Option[String]): ScalaTestClassesItem = { + copy(framework = framework) + } + def withFramework(framework: String): ScalaTestClassesItem = { + copy(framework = Option(framework)) + } } object ScalaTestClassesItem { - def apply(target: sbt.internal.bsp.BuildTargetIdentifier, classes: Vector[String]): ScalaTestClassesItem = new ScalaTestClassesItem(target, classes) + def apply(target: sbt.internal.bsp.BuildTargetIdentifier, classes: Vector[String], framework: Option[String]): ScalaTestClassesItem = new ScalaTestClassesItem(target, classes, framework) + def apply(target: sbt.internal.bsp.BuildTargetIdentifier, classes: Vector[String], framework: String): ScalaTestClassesItem = new ScalaTestClassesItem(target, classes, Option(framework)) } diff --git a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaTestClassesItemFormats.scala b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaTestClassesItemFormats.scala index a079301b3..1353a0b41 100644 --- a/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaTestClassesItemFormats.scala +++ b/protocol/src/main/contraband-scala/sbt/internal/bsp/codec/ScalaTestClassesItemFormats.scala @@ -13,8 +13,9 @@ implicit lazy val ScalaTestClassesItemFormat: JsonFormat[sbt.internal.bsp.ScalaT unbuilder.beginObject(__js) val target = unbuilder.readField[sbt.internal.bsp.BuildTargetIdentifier]("target") val classes = unbuilder.readField[Vector[String]]("classes") + val framework = unbuilder.readField[Option[String]]("framework") unbuilder.endObject() - sbt.internal.bsp.ScalaTestClassesItem(target, classes) + sbt.internal.bsp.ScalaTestClassesItem(target, classes, framework) case None => deserializationError("Expected JsObject but found None") } @@ -23,6 +24,7 @@ implicit lazy val ScalaTestClassesItemFormat: JsonFormat[sbt.internal.bsp.ScalaT builder.beginObject() builder.addField("target", obj.target) builder.addField("classes", obj.classes) + builder.addField("framework", obj.framework) builder.endObject() } } diff --git a/protocol/src/main/contraband/bsp.contra b/protocol/src/main/contraband/bsp.contra index 01b3a450f..62438cff0 100644 --- a/protocol/src/main/contraband/bsp.contra +++ b/protocol/src/main/contraband/bsp.contra @@ -636,6 +636,9 @@ type ScalaTestClassesItem { ## The fully qualified names of the test classes in this target classes: [String] + + ## The name of the test framework used in test classes. + framework: String } ## Scala Main Class Request From 1396e1605d04dc53b91e41c1cc1be2f339e310ec Mon Sep 17 00:00:00 2001 From: Adrien Piquerez Date: Tue, 8 Mar 2022 18:19:20 +0100 Subject: [PATCH 2/3] Try fix CI: Build and test (6) --- project/Dependencies.scala | 14 +++++++------- project/plugins.sbt | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 84413f9d7..ae0d47987 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -69,13 +69,13 @@ object Dependencies { def addSbtLmIvy = addSbtModule(sbtLmPath, "lmIvy", libraryManagementIvy) def addSbtLmIvyTest = addSbtModule(sbtLmPath, "lmIvy", libraryManagementIvy, Some(Test)) - def addSbtCompilerInterface = addSbtModule(sbtZincPath, "compilerInterfaceJVM", compilerInterface) - def addSbtCompilerClasspath = addSbtModule(sbtZincPath, "zincClasspathJVM2_12", compilerClasspath) - def addSbtCompilerApiInfo = addSbtModule(sbtZincPath, "zincApiInfoJVM2_12", compilerApiInfo) - def addSbtCompilerBridge = addSbtModule(sbtZincPath, "compilerBridgeJVM2_12", compilerBridge) - def addSbtZinc = addSbtModule(sbtZincPath, "zincJVM2_12", zinc) - def addSbtZincCompile = addSbtModule(sbtZincPath, "zincCompileJVM2_12", zincCompile) - def addSbtZincCompileCore = addSbtModule(sbtZincPath, "zincCompileCoreJVM2_12", zincCompileCore) + def addSbtCompilerInterface = addSbtModule(sbtZincPath, "compilerInterface", compilerInterface) + def addSbtCompilerClasspath = addSbtModule(sbtZincPath, "zincClasspath", compilerClasspath) + def addSbtCompilerApiInfo = addSbtModule(sbtZincPath, "zincApiInfo", compilerApiInfo) + def addSbtCompilerBridge = addSbtModule(sbtZincPath, "compilerBridge2_12", compilerBridge) + def addSbtZinc = addSbtModule(sbtZincPath, "zinc", zinc) + def addSbtZincCompile = addSbtModule(sbtZincPath, "zincCompile", zincCompile) + def addSbtZincCompileCore = addSbtModule(sbtZincPath, "zincCompileCore", zincCompileCore) val lmCoursierShaded = "io.get-coursier" %% "lm-coursier-shaded" % "2.0.10" diff --git a/project/plugins.sbt b/project/plugins.sbt index 7f7e8fa9a..00c24984e 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -6,9 +6,9 @@ addSbtPlugin("com.dwijnand" % "sbt-dynver" % "4.0.0") addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.1.2") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.3.0") addSbtPlugin("org.scala-sbt" % "sbt-contraband" % "0.5.1") -addSbtPlugin("de.heikoseeberger" % "sbt-header" % "3.0.2") +addSbtPlugin("de.heikoseeberger" % "sbt-header" % "5.6.5") addSbtPlugin("com.lightbend" % "sbt-whitesource" % "0.1.14") -addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.9") +addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "1.2.0") addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.8.1") addSbtPlugin("com.swoval" % "sbt-java-format" % "0.3.1") addDependencyTreePlugin From ce978a19ed39bc69e7b0c3debeb2330f639011f8 Mon Sep 17 00:00:00 2001 From: Kamil Podsiadlo Date: Thu, 10 Mar 2022 19:56:49 +0100 Subject: [PATCH 3/3] tests: add test case for framework field in scala test classes request --- .../internal/server/BuildServerProtocol.scala | 32 ++++++++----------- .../test/scala/testpkg/BuildServerTest.scala | 3 +- .../src/main/scala/sbt/TestFramework.scala | 9 ++++-- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala b/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala index d289884bf..0c6655e2d 100644 --- a/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala +++ b/main/src/main/scala/sbt/internal/server/BuildServerProtocol.scala @@ -39,6 +39,7 @@ import scala.collection.mutable import scala.util.control.NonFatal import scala.util.{ Failure, Success, Try } import scala.annotation.nowarn +import sbt.testing.Framework object BuildServerProtocol { import sbt.internal.bsp.codec.JsonProtocol._ @@ -844,25 +845,20 @@ object BuildServerProtocol { Keys.definedTests.?.value match { case None => Vector.empty case Some(definitions) => - val fingerprints = Keys.loadedTestFrameworks.?.value - .getOrElse(Map.empty) - .values - .flatMap { framework => - framework.fingerprints().map(fingerprint => (fingerprint, framework)) - } - .toMap + val frameworks: Seq[Framework] = Keys.loadedTestFrameworks.?.value + .map(_.values.toSeq) + .getOrElse(Seq.empty) - definitions - .groupBy(defn => fingerprints.get(defn.fingerprint)) - .map { - case (framework, definitions) => - ScalaTestClassesItem( - bspTargetIdentifier.value, - definitions.map(_.name).toVector, - framework.map(_.name()) - ) - } - .toSeq + val grouped = TestFramework.testMap(frameworks, definitions) + + grouped.map { + case (framework, definitions) => + ScalaTestClassesItem( + bspTargetIdentifier.value, + definitions.map(_.name).toVector, + framework.name() + ) + }.toSeq } } diff --git a/server-test/src/test/scala/testpkg/BuildServerTest.scala b/server-test/src/test/scala/testpkg/BuildServerTest.scala index 5aa231752..9cbe43980 100644 --- a/server-test/src/test/scala/testpkg/BuildServerTest.scala +++ b/server-test/src/test/scala/testpkg/BuildServerTest.scala @@ -308,7 +308,8 @@ object BuildServerTest extends AbstractServerTest { assert(svr.waitForString(10.seconds) { s => (s contains """"id":"72"""") && (s contains """"tests.FailingTest"""") && - (s contains """"tests.PassingTest"""") + (s contains """"tests.PassingTest"""") && + (s contains """"framework":"ScalaTest"""") }) } diff --git a/testing/src/main/scala/sbt/TestFramework.scala b/testing/src/main/scala/sbt/TestFramework.scala index fe3f76196..cb89dd5f4 100644 --- a/testing/src/main/scala/sbt/TestFramework.scala +++ b/testing/src/main/scala/sbt/TestFramework.scala @@ -231,21 +231,26 @@ object TestFramework { ): Vector[(String, TestFunction)] = for (d <- inputs; act <- mapped.get(d.name)) yield (d.name, act) - private[this] def testMap( + def testMap( frameworks: Seq[Framework], tests: Seq[TestDefinition] ): Map[Framework, Set[TestDefinition]] = { import scala.collection.mutable.{ HashMap, HashSet, Set } val map = new HashMap[Framework, Set[TestDefinition]] + def assignTest(test: TestDefinition): Unit = { def isTestForFramework(framework: Framework) = getFingerprints(framework).exists { t => matches(t, test.fingerprint) } - for (framework <- frameworks.find(isTestForFramework)) + + frameworks.find(isTestForFramework).foreach { framework => map.getOrElseUpdate(framework, new HashSet[TestDefinition]) += test + } } + if (frameworks.nonEmpty) for (test <- tests) assignTest(test) + map.toMap.mapValues(_.toSet).toMap }