diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 338e3267dd..0c00cadc7e 100644 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -1310,7 +1310,8 @@ object Defaults extends BuildCommon { testListeners :== Nil, testOptions :== Nil, testResultLogger :== TestResultLogger.Default, - testOnly / testFilter :== (IncrementalTest.selectedFilter _) + testOnly / testFilter :== (IncrementalTest.selectedFilter _), + extraTestDigests :== Nil, ) ) lazy val testTasks: Seq[Setting[_]] = @@ -1333,6 +1334,7 @@ object Defaults extends BuildCommon { .triggeredBy(compile) .value, testQuick / testFilter := IncrementalTest.filterTask.value, + extraTestDigests ++= IncrementalTest.extraTestDigestsTask.value, executeTests := { import sbt.TupleSyntax.* ( diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index 5e4d616e86..a88fcaf9ef 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -366,6 +366,7 @@ object Keys { val testResultLogger = settingKey[TestResultLogger]("Logs results after a test task completes.").withRank(DTask) val testGrouping = taskKey[Seq[Tests.Group]]("Collects discovered tests into groups. Whether to fork and the options for forking are configurable on a per-group basis.").withRank(BMinusTask) val isModule = AttributeKey[Boolean]("isModule", "True if the target is a module.", DSetting) + val extraTestDigests = taskKey[Seq[Digest]]("Extra digests that would invalidate test caching").withRank(DTask) // Classpath/Dependency Management Keys type Classpath = Def.Classpath diff --git a/main/src/main/scala/sbt/internal/IncrementalTest.scala b/main/src/main/scala/sbt/internal/IncrementalTest.scala index 63697f9a47..68bf7ab75d 100644 --- a/main/src/main/scala/sbt/internal/IncrementalTest.scala +++ b/main/src/main/scala/sbt/internal/IncrementalTest.scala @@ -11,7 +11,7 @@ package internal import java.io.File import java.util.concurrent.ConcurrentHashMap -import Keys.{ test, compileInputs, fileConverter, fullClasspath, streams } +import Keys.{ test, fileConverter, fullClasspath, streams } import sbt.Def.Initialize import sbt.internal.inc.Analysis import sbt.internal.util.Attributed @@ -49,16 +49,7 @@ object IncrementalTest: val cp = (Keys.test / fullClasspath).value val testNames = Keys.definedTests.value.map(_.name).toVector.distinct val converter = fileConverter.value - val sv = Keys.scalaVersion.value - val inputs = (Keys.compile / Keys.compileInputs).value - // by default this captures JVM version - val extraInc = Keys.extraIncOptions.value - // throw in any information useful for runtime invalidation - val salt = s"""$sv -${converter.toVirtualFile(inputs.options.classesDirectory)} -${extraInc.mkString(",")} -""" - val extra = Vector(Digest.sha256Hash(salt.getBytes("UTF-8"))) + val extra = Keys.extraTestDigests.value val stamper = ClassStamper(cp, converter) // TODO: Potentially do something about JUnit 5 and others which might not use class name Map((testNames.flatMap: name => @@ -68,6 +59,15 @@ ${extraInc.mkString(",")} ): _*) } + def extraTestDigestsTask: Initialize[Task[Seq[Digest]]] = Def.cachedTask { + // by default this captures JVM version + val extraInc = Keys.extraIncOptions.value + // throw in any information useful for runtime invalidation + val salt = s"""${extraInc.mkString(",")} +""" + Vector(Digest.sha256Hash(salt.getBytes("UTF-8"))) + } + def selectedFilter(args: Seq[String]): Seq[String => Boolean] = def matches(nfs: Seq[NameFilter], s: String) = nfs.exists(_.accept(s)) val (excludeArgs, includeArgs) = args.partition(_.startsWith("-"))