Skip to content

Commit 13efc9f

Browse files
Consider targets used for test discovery in dependency checking
Before, we did test discovery at runtime, but we now do it at compile-time, for speed and to avoid having to serialize the analysis information returned by the compiler bridge. This commits marks targets used during test discovery as used, thereby subjecting them to depednency checking. In most cases, this won't make a difference as the target containing the implementation of the SBT test interface is the same one that the target being built would've depended on anyway. One important thing to note is that this will break a property of our dependency checker that's been thus-far guaranteed: that it only allows a dependency to be in `deps` or not in `deps`. When we do test discovery, iterate through a list of test "frameworks". If we find a test framework in the classpath, we look for tests that use that framework; otherwise, we don't. This means that, for example, if specs2 isn't on the classpath, but the target contains specs2 tests, the tests will silently fail to execute.
1 parent a11b1df commit 13efc9f

File tree

3 files changed

+41
-25
lines changed

3 files changed

+41
-25
lines changed

src/main/scala/higherkindness/rules_scala/common/sbt-testing/Test.scala

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package higherkindness.rules_scala.common.sbt_testing
22

3+
import java.nio.file.{Path, Paths}
34
import play.api.libs.json.{Format, Json}
45
import sbt.testing.{Event, Framework, Logger, Runner, Status, Task, TaskDef, TestWildcardSelector}
56
import scala.collection.mutable
@@ -12,10 +13,27 @@ object TestDefinition {
1213
}
1314

1415
class TestFrameworkLoader(loader: ClassLoader) {
16+
private val loadedJars = mutable.ArrayBuffer[Path]()
17+
private def getClassJar(`class`: Class[?]): Option[Path] = for {
18+
codeSource <- Option(`class`.getProtectionDomain().getCodeSource())
19+
codeSourceUri = codeSource.getLocation().toURI()
20+
path <-
21+
try {
22+
Some(Paths.get(codeSourceUri))
23+
} catch {
24+
case _: IllegalArgumentException => None
25+
}
26+
} yield path
27+
28+
def getLoadedJars(): Array[Path] = loadedJars.toArray
1529
def load(className: String) = {
1630
val framework =
1731
try {
18-
Some(Class.forName(className, true, loader).getDeclaredConstructor().newInstance())
32+
val `class` = Class.forName(className, true, loader)
33+
34+
getClassJar(`class`).foreach(loadedJars += _)
35+
36+
Some(`class`.getDeclaredConstructor().newInstance())
1937
} catch {
2038
case _: ClassNotFoundException => None
2139
case NonFatal(e) => throw new Exception(s"Failed to load framework $className", e)

src/main/scala/higherkindness/rules_scala/workers/zinc/compile/ZincRunner.scala

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -195,19 +195,25 @@ object ZincRunner extends WorkerMain[Unit] {
195195
}
196196
}
197197

198-
private def maybeWriteTestsFile(parsedArguments: CommonArguments, analysis: AnnexAnalysis): Unit =
199-
parsedArguments.testsFile.foreach { path =>
200-
val classloader = ClassLoaders.sbtTestClassLoader(parsedArguments.classpath.map(_.toUri.toURL))
201-
val frameworkLoader = new TestFrameworkLoader(classloader)
202-
val testsFileData = TestsFileData(
203-
parsedArguments.testFrameworks
204-
.flatMap(frameworkName => frameworkLoader.load(frameworkName).map((frameworkName, _)))
205-
.map { case (frameworkName, framework) => frameworkName -> new TestDiscovery(framework)(analysis.apis.toSet) }
206-
.toMap,
207-
)
208-
209-
Files.write(path, Json.stringify(Json.toJson(testsFileData)).getBytes(StandardCharsets.UTF_8))
210-
}
198+
private def maybeWriteTestsFile(parsedArguments: CommonArguments, analysis: AnnexAnalysis): Array[Path] =
199+
parsedArguments.testsFile
200+
.map { path =>
201+
val classloader = ClassLoaders.sbtTestClassLoader(parsedArguments.classpath.map(_.toUri.toURL))
202+
val frameworkLoader = new TestFrameworkLoader(classloader)
203+
val testsFileData = TestsFileData(
204+
parsedArguments.testFrameworks
205+
.flatMap(frameworkName => frameworkLoader.load(frameworkName).map((frameworkName, _)))
206+
.map { case (frameworkName, framework) =>
207+
frameworkName -> new TestDiscovery(framework)(analysis.apis.toSet)
208+
}
209+
.toMap,
210+
)
211+
212+
Files.write(path, Json.stringify(Json.toJson(testsFileData)).getBytes(StandardCharsets.UTF_8))
213+
214+
frameworkLoader.getLoadedJars()
215+
}
216+
.getOrElse(Array.empty)
211217

212218
protected def init(args: Option[Array[String]]): Unit = ()
213219

@@ -282,7 +288,7 @@ object ZincRunner extends WorkerMain[Unit] {
282288

283289
InterruptUtil.throwIfInterrupted(task.isCancelled)
284290

285-
maybeWriteTestsFile(workRequest, analysis)
291+
val usedDepsDuringTestDiscovery = maybeWriteTestsFile(workRequest, analysis)
286292

287293
InterruptUtil.throwIfInterrupted(task.isCancelled)
288294

@@ -294,7 +300,7 @@ object ZincRunner extends WorkerMain[Unit] {
294300
val usedDeps = workRequest.classpath.view
295301
.map(_.normalize().toAbsolutePath)
296302
.toSet
297-
.intersect(analysis.usedJars.toSet)
303+
.intersect((analysis.usedJars.view ++ usedDepsDuringTestDiscovery.view).toSet)
298304
// Filter out the Scala standard library as they should always be implicitly available we shouldn't be
299305
// bookkeeping them
300306
.view

tests/test-frameworks/mixed/BUILD

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,12 @@ load("@rules_scala_annex//rules:scala.bzl", "scala_test")
33
scala_test(
44
name = "mixed_2_13",
55
srcs = glob(["*.scala"]),
6-
deps_used_whitelist = [
7-
"@annex_test//:org_hamcrest_hamcrest_core",
8-
"@annex_test//:com_novocode_junit_interface",
9-
],
106
scala_toolchain_name = "test_zinc_2_13",
117
shard_count = 2,
128
tags = ["manual"],
139
deps = [
1410
"@annex_test//:com_novocode_junit_interface",
1511
"@annex_test//:junit_junit",
16-
"@annex_test//:org_hamcrest_hamcrest_core",
1712
"@annex_test//:org_scalacheck_scalacheck_2_13",
1813
"@annex_test//:org_scalactic_scalactic_2_13",
1914
"@annex_test//:org_scalatest_scalatest_compatible",
@@ -31,11 +26,8 @@ scala_test(
3126
scala_toolchain_name = "test_zinc_2_12",
3227
shard_count = 2,
3328
tags = ["manual"],
34-
runtime_deps = [
35-
"@annex_test//:com_novocode_junit_interface",
36-
"@annex_test//:org_hamcrest_hamcrest_core",
37-
],
3829
deps = [
30+
"@annex_test//:com_novocode_junit_interface",
3931
"@annex_test//:junit_junit",
4032
"@annex_test_2_12//:org_scalacheck_scalacheck_2_12",
4133
"@annex_test_2_12//:org_scalactic_scalactic_2_12",

0 commit comments

Comments
 (0)