mirror of https://github.com/sbt/sbt.git
Refactor to definedTestDigests task
This commit is contained in:
parent
6952d3c46d
commit
0021c3a0bd
|
|
@ -307,6 +307,7 @@ object Tests {
|
|||
in.filter(t => seen.add(f(t)))
|
||||
}
|
||||
|
||||
// Called by Defaults
|
||||
def apply(
|
||||
frameworks: Map[TestFramework, Framework],
|
||||
testLoader: ClassLoader,
|
||||
|
|
@ -340,7 +341,7 @@ object Tests {
|
|||
apply(frameworks, testLoader, runners, o, config, log)
|
||||
}
|
||||
|
||||
def testTask(
|
||||
private[sbt] def testTask(
|
||||
loader: ClassLoader,
|
||||
frameworks: Map[TestFramework, Framework],
|
||||
runners: Map[TestFramework, Runner],
|
||||
|
|
|
|||
|
|
@ -1329,6 +1329,9 @@ object Defaults extends BuildCommon {
|
|||
.storeAs(definedTestNames)
|
||||
.triggeredBy(compile)
|
||||
.value,
|
||||
definedTestDigests := IncrementalTest.definedTestDigestTask
|
||||
.triggeredBy(compile)
|
||||
.value,
|
||||
testQuick / testFilter := IncrementalTest.filterTask.value,
|
||||
executeTests := {
|
||||
import sbt.TupleSyntax.*
|
||||
|
|
@ -1411,8 +1414,7 @@ object Defaults extends BuildCommon {
|
|||
) +:
|
||||
TestStatusReporter(
|
||||
IncrementalTest.succeededFile((test / streams).value.cacheDirectory),
|
||||
(Keys.test / fullClasspath).value,
|
||||
fileConverter.value,
|
||||
definedTestDigests.value,
|
||||
) +:
|
||||
(TaskZero / testListeners).value
|
||||
},
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ import sbt.librarymanagement._
|
|||
import sbt.librarymanagement.ivy.{ Credentials, IvyConfiguration, IvyPaths, UpdateOptions }
|
||||
import sbt.nio.file.Glob
|
||||
import sbt.testing.Framework
|
||||
import sbt.util.{ cacheLevel, ActionCacheStore, Level, Logger, LoggerContext }
|
||||
import sbt.util.{ cacheLevel, ActionCacheStore, Digest, Level, Logger, LoggerContext }
|
||||
import xsbti.{ HashedVirtualFileRef, VirtualFile, VirtualFileRef }
|
||||
import xsbti.compile._
|
||||
import xsbti.compile.analysis.ReadStamps
|
||||
|
|
@ -349,8 +349,10 @@ object Keys {
|
|||
// Test Keys
|
||||
val testLoader = taskKey[ClassLoader]("Provides the class loader used for testing.").withRank(DTask)
|
||||
val loadedTestFrameworks = taskKey[Map[TestFramework, Framework]]("Loads Framework definitions from the test loader.").withRank(DTask)
|
||||
@cacheLevel(include = Array.empty)
|
||||
val definedTests = taskKey[Seq[TestDefinition]]("Provides the list of defined tests.").withRank(BMinusTask)
|
||||
val definedTestNames = taskKey[Seq[String]]("Provides the set of defined test names.").withRank(BMinusTask)
|
||||
val definedTestDigests = taskKey[Map[String, Digest]]("Provides a unique digest of defined tests.").withRank(DTask)
|
||||
val executeTests = taskKey[Tests.Output]("Executes all tests, producing a report.").withRank(CTask)
|
||||
val test = taskKey[Unit]("Executes all tests.").withRank(APlusTask)
|
||||
val testOnly = inputKey[Unit]("Executes the tests provided as arguments or all tests if no arguments are provided.").withRank(ATask)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -32,17 +32,30 @@ object IncrementalTest:
|
|||
Def.task {
|
||||
val cp = (Keys.test / fullClasspath).value
|
||||
val s = (Keys.test / streams).value
|
||||
val converter = fileConverter.value
|
||||
val stamper = ClassStamper(cp, converter)
|
||||
val digests = (Keys.definedTestDigests).value
|
||||
val succeeded = TestStatus.read(succeededFile(s.cacheDirectory))
|
||||
def hasSucceeded(className: String): Boolean = succeeded.get(className) match
|
||||
case None => false
|
||||
case Some(ts) => ts == stamper.transitiveStamp(className)
|
||||
case Some(ts) => Some(ts) == digests.get(className)
|
||||
args =>
|
||||
for filter <- selectedFilter(args)
|
||||
yield (test: String) => filter(test) && !hasSucceeded(test)
|
||||
}
|
||||
|
||||
// cache the test digests against the fullClasspath.
|
||||
def definedTestDigestTask: Initialize[Task[Map[String, Digest]]] = Def.cachedTask {
|
||||
val cp = (Keys.test / fullClasspath).value
|
||||
val testNames = Keys.definedTests.value.map(_.name).toVector.distinct
|
||||
val converter = fileConverter.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 =>
|
||||
stamper.transitiveStamp(name) match
|
||||
case Some(ts) => Seq(name -> ts)
|
||||
case None => Nil
|
||||
): _*)
|
||||
}
|
||||
|
||||
def succeededFile(dir: File): File = dir / "succeeded_tests.txt"
|
||||
|
||||
def selectedFilter(args: Seq[String]): Seq[String => Boolean] =
|
||||
|
|
@ -123,9 +136,10 @@ class ClassStamper(
|
|||
/**
|
||||
* Given a classpath and a class name, this tries to create a SHA-256 digest.
|
||||
*/
|
||||
def transitiveStamp(className: String): Digest =
|
||||
def transitiveStamp(className: String): Option[Digest] =
|
||||
val digests = SortedSet(analyses.flatMap(internalStamp(className, _, Set.empty)): _*)
|
||||
Digest.sha256Hash(digests.toSeq: _*)
|
||||
if digests.nonEmpty then Some(Digest.sha256Hash(digests.toSeq: _*))
|
||||
else None
|
||||
|
||||
private def internalStamp(
|
||||
className: String,
|
||||
|
|
@ -144,7 +158,7 @@ class ClassStamper(
|
|||
internalStamp(otherCN, analysis, alreadySeen + className)
|
||||
val internalJarDeps = relations
|
||||
.externalDeps(className)
|
||||
.map: libClassName =>
|
||||
.flatMap: libClassName =>
|
||||
transitiveStamp(libClassName)
|
||||
val externalDeps = relations
|
||||
.externalDeps(className)
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ final class TestFramework(val implClassNames: String*) extends Serializable {
|
|||
def create(loader: ClassLoader, log: ManagedLogger): Option[Framework] =
|
||||
createFramework(loader, log, implClassNames.toList)
|
||||
}
|
||||
|
||||
final class TestDefinition(
|
||||
val name: String,
|
||||
val fingerprint: Fingerprint,
|
||||
|
|
@ -108,7 +109,7 @@ final class TestDefinition(
|
|||
override def hashCode: Int = (name.hashCode, TestFramework.hashCode(fingerprint)).hashCode
|
||||
}
|
||||
|
||||
final class TestRunner(
|
||||
private[sbt] final class TestRunner(
|
||||
delegate: Runner,
|
||||
listeners: Vector[TestReportListener],
|
||||
log: ManagedLogger
|
||||
|
|
@ -214,7 +215,7 @@ object TestFramework {
|
|||
case _ => f.toString
|
||||
}
|
||||
|
||||
def testTasks(
|
||||
private[sbt] def testTasks(
|
||||
frameworks: Map[TestFramework, Framework],
|
||||
runners: Map[TestFramework, Runner],
|
||||
testLoader: ClassLoader,
|
||||
|
|
|
|||
Loading…
Reference in New Issue