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)))
|
in.filter(t => seen.add(f(t)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Called by Defaults
|
||||||
def apply(
|
def apply(
|
||||||
frameworks: Map[TestFramework, Framework],
|
frameworks: Map[TestFramework, Framework],
|
||||||
testLoader: ClassLoader,
|
testLoader: ClassLoader,
|
||||||
|
|
@ -340,7 +341,7 @@ object Tests {
|
||||||
apply(frameworks, testLoader, runners, o, config, log)
|
apply(frameworks, testLoader, runners, o, config, log)
|
||||||
}
|
}
|
||||||
|
|
||||||
def testTask(
|
private[sbt] def testTask(
|
||||||
loader: ClassLoader,
|
loader: ClassLoader,
|
||||||
frameworks: Map[TestFramework, Framework],
|
frameworks: Map[TestFramework, Framework],
|
||||||
runners: Map[TestFramework, Runner],
|
runners: Map[TestFramework, Runner],
|
||||||
|
|
|
||||||
|
|
@ -1329,6 +1329,9 @@ object Defaults extends BuildCommon {
|
||||||
.storeAs(definedTestNames)
|
.storeAs(definedTestNames)
|
||||||
.triggeredBy(compile)
|
.triggeredBy(compile)
|
||||||
.value,
|
.value,
|
||||||
|
definedTestDigests := IncrementalTest.definedTestDigestTask
|
||||||
|
.triggeredBy(compile)
|
||||||
|
.value,
|
||||||
testQuick / testFilter := IncrementalTest.filterTask.value,
|
testQuick / testFilter := IncrementalTest.filterTask.value,
|
||||||
executeTests := {
|
executeTests := {
|
||||||
import sbt.TupleSyntax.*
|
import sbt.TupleSyntax.*
|
||||||
|
|
@ -1411,8 +1414,7 @@ object Defaults extends BuildCommon {
|
||||||
) +:
|
) +:
|
||||||
TestStatusReporter(
|
TestStatusReporter(
|
||||||
IncrementalTest.succeededFile((test / streams).value.cacheDirectory),
|
IncrementalTest.succeededFile((test / streams).value.cacheDirectory),
|
||||||
(Keys.test / fullClasspath).value,
|
definedTestDigests.value,
|
||||||
fileConverter.value,
|
|
||||||
) +:
|
) +:
|
||||||
(TaskZero / testListeners).value
|
(TaskZero / testListeners).value
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ import sbt.librarymanagement._
|
||||||
import sbt.librarymanagement.ivy.{ Credentials, IvyConfiguration, IvyPaths, UpdateOptions }
|
import sbt.librarymanagement.ivy.{ Credentials, IvyConfiguration, IvyPaths, UpdateOptions }
|
||||||
import sbt.nio.file.Glob
|
import sbt.nio.file.Glob
|
||||||
import sbt.testing.Framework
|
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.{ HashedVirtualFileRef, VirtualFile, VirtualFileRef }
|
||||||
import xsbti.compile._
|
import xsbti.compile._
|
||||||
import xsbti.compile.analysis.ReadStamps
|
import xsbti.compile.analysis.ReadStamps
|
||||||
|
|
@ -349,8 +349,10 @@ object Keys {
|
||||||
// Test Keys
|
// Test Keys
|
||||||
val testLoader = taskKey[ClassLoader]("Provides the class loader used for testing.").withRank(DTask)
|
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)
|
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 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 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 executeTests = taskKey[Tests.Output]("Executes all tests, producing a report.").withRank(CTask)
|
||||||
val test = taskKey[Unit]("Executes all tests.").withRank(APlusTask)
|
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)
|
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.io.File
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import Keys.{ test, compileInputs, fileConverter, fullClasspath, streams }
|
import Keys.{ test, fileConverter, fullClasspath, streams }
|
||||||
import sbt.Def.Initialize
|
import sbt.Def.Initialize
|
||||||
import sbt.internal.inc.Analysis
|
import sbt.internal.inc.Analysis
|
||||||
import sbt.internal.util.Attributed
|
import sbt.internal.util.Attributed
|
||||||
|
|
@ -32,17 +32,30 @@ object IncrementalTest:
|
||||||
Def.task {
|
Def.task {
|
||||||
val cp = (Keys.test / fullClasspath).value
|
val cp = (Keys.test / fullClasspath).value
|
||||||
val s = (Keys.test / streams).value
|
val s = (Keys.test / streams).value
|
||||||
val converter = fileConverter.value
|
val digests = (Keys.definedTestDigests).value
|
||||||
val stamper = ClassStamper(cp, converter)
|
|
||||||
val succeeded = TestStatus.read(succeededFile(s.cacheDirectory))
|
val succeeded = TestStatus.read(succeededFile(s.cacheDirectory))
|
||||||
def hasSucceeded(className: String): Boolean = succeeded.get(className) match
|
def hasSucceeded(className: String): Boolean = succeeded.get(className) match
|
||||||
case None => false
|
case None => false
|
||||||
case Some(ts) => ts == stamper.transitiveStamp(className)
|
case Some(ts) => Some(ts) == digests.get(className)
|
||||||
args =>
|
args =>
|
||||||
for filter <- selectedFilter(args)
|
for filter <- selectedFilter(args)
|
||||||
yield (test: String) => filter(test) && !hasSucceeded(test)
|
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 succeededFile(dir: File): File = dir / "succeeded_tests.txt"
|
||||||
|
|
||||||
def selectedFilter(args: Seq[String]): Seq[String => Boolean] =
|
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.
|
* 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)): _*)
|
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(
|
private def internalStamp(
|
||||||
className: String,
|
className: String,
|
||||||
|
|
@ -144,7 +158,7 @@ class ClassStamper(
|
||||||
internalStamp(otherCN, analysis, alreadySeen + className)
|
internalStamp(otherCN, analysis, alreadySeen + className)
|
||||||
val internalJarDeps = relations
|
val internalJarDeps = relations
|
||||||
.externalDeps(className)
|
.externalDeps(className)
|
||||||
.map: libClassName =>
|
.flatMap: libClassName =>
|
||||||
transitiveStamp(libClassName)
|
transitiveStamp(libClassName)
|
||||||
val externalDeps = relations
|
val externalDeps = relations
|
||||||
.externalDeps(className)
|
.externalDeps(className)
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,7 @@ final class TestFramework(val implClassNames: String*) extends Serializable {
|
||||||
def create(loader: ClassLoader, log: ManagedLogger): Option[Framework] =
|
def create(loader: ClassLoader, log: ManagedLogger): Option[Framework] =
|
||||||
createFramework(loader, log, implClassNames.toList)
|
createFramework(loader, log, implClassNames.toList)
|
||||||
}
|
}
|
||||||
|
|
||||||
final class TestDefinition(
|
final class TestDefinition(
|
||||||
val name: String,
|
val name: String,
|
||||||
val fingerprint: Fingerprint,
|
val fingerprint: Fingerprint,
|
||||||
|
|
@ -108,7 +109,7 @@ final class TestDefinition(
|
||||||
override def hashCode: Int = (name.hashCode, TestFramework.hashCode(fingerprint)).hashCode
|
override def hashCode: Int = (name.hashCode, TestFramework.hashCode(fingerprint)).hashCode
|
||||||
}
|
}
|
||||||
|
|
||||||
final class TestRunner(
|
private[sbt] final class TestRunner(
|
||||||
delegate: Runner,
|
delegate: Runner,
|
||||||
listeners: Vector[TestReportListener],
|
listeners: Vector[TestReportListener],
|
||||||
log: ManagedLogger
|
log: ManagedLogger
|
||||||
|
|
@ -214,7 +215,7 @@ object TestFramework {
|
||||||
case _ => f.toString
|
case _ => f.toString
|
||||||
}
|
}
|
||||||
|
|
||||||
def testTasks(
|
private[sbt] def testTasks(
|
||||||
frameworks: Map[TestFramework, Framework],
|
frameworks: Map[TestFramework, Framework],
|
||||||
runners: Map[TestFramework, Runner],
|
runners: Map[TestFramework, Runner],
|
||||||
testLoader: ClassLoader,
|
testLoader: ClassLoader,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue