Skip to content

Commit

Permalink
Include setup and cleanup digests
Browse files Browse the repository at this point in the history
  • Loading branch information
eed3si9n committed Sep 16, 2024
1 parent a53382b commit f2cbb7b
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 16 deletions.
53 changes: 43 additions & 10 deletions main-actions/src/main/scala/sbt/Tests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ import testing.{

import scala.annotation.tailrec
import scala.util.control.NonFatal
import scala.quoted.*
import sbt.internal.util.ManagedLogger
import sbt.util.Logger
import sbt.util.{ Digest, Logger }
import sbt.protocol.testing.TestResult

import scala.runtime.AbstractFunction3
Expand Down Expand Up @@ -68,26 +69,58 @@ object Tests {
* The ClassLoader provided to `setup` is the loader containing the test classes that will be run.
* Setup is not currently performed for forked tests.
*/
final case class Setup(setup: ClassLoader => Unit) extends TestOption
final case class Setup(setup: ClassLoader => Unit, codeDigest: Digest) extends TestOption

/**
* Defines a TestOption that will evaluate `setup` before any tests execute.
* Setup is not currently performed for forked tests.
*/
def Setup(setup: () => Unit) = new Setup(_ => setup())
inline def Setup(inline setup: () => Unit): Setup = ${ unitSetupMacro('setup) }

def unitSetupMacro(fn: Expr[() => Unit])(using Quotes): Expr[Setup] =
val codeDigest = Digest.sha256Hash(fn.show.getBytes("UTF-8"))
val codeDigestStr = Expr(codeDigest.toString())
'{
new Setup(_ => $fn(), Digest($codeDigestStr))
}

inline def Setup(inline setup: ClassLoader => Unit): Setup = ${ clSetupMacro('setup) }

def clSetupMacro(fn: Expr[ClassLoader => Unit])(using Quotes): Expr[Setup] =
val codeDigest = Digest.sha256Hash(fn.show.getBytes("UTF-8"))
val codeDigestStr = Expr(codeDigest.toString())
'{
new Setup($fn, Digest($codeDigestStr))
}

/**
* Defines a TestOption that will evaluate `cleanup` after all tests execute.
* The ClassLoader provided to `cleanup` is the loader containing the test classes that ran.
* Cleanup is not currently performed for forked tests.
*/
final case class Cleanup(cleanup: ClassLoader => Unit) extends TestOption
final case class Cleanup(cleanup: ClassLoader => Unit, codeDigest: Digest) extends TestOption

/**
* Defines a TestOption that will evaluate `cleanup` after all tests execute.
* Cleanup is not currently performed for forked tests.
*/
def Cleanup(cleanup: () => Unit) = new Cleanup(_ => cleanup())
inline def Cleanup(inline cleanup: () => Unit) = ${ unitCleanupMacro('cleanup) }

def unitCleanupMacro(fn: Expr[() => Unit])(using Quotes): Expr[Cleanup] =
val codeDigest = Digest.sha256Hash(fn.show.getBytes("UTF-8"))
val codeDigestStr = Expr(codeDigest.toString())
'{
new Cleanup(_ => $fn(), Digest($codeDigestStr))
}

inline def Cleanup(inline cleanup: ClassLoader => Unit): Cleanup = ${ clCleanupMacro('cleanup) }

def clCleanupMacro(fn: Expr[ClassLoader => Unit])(using Quotes): Expr[Cleanup] =
val codeDigest = Digest.sha256Hash(fn.show.getBytes("UTF-8"))
val codeDigestStr = Expr(codeDigest.toString())
'{
new Cleanup($fn, Digest($codeDigestStr))
}

/** The names of tests to explicitly exclude from execution. */
final case class Exclude(tests: Iterable[String]) extends TestOption
Expand Down Expand Up @@ -271,11 +304,11 @@ object Tests {
if (orderedFilters.nonEmpty) sys.error("Cannot define multiple ordered test filters.")
else orderedFilters = includes
()
case Exclude(exclude) => excludeTestsSet ++= exclude; ()
case Listeners(listeners) => testListeners ++= listeners; ()
case Setup(setupFunction) => setup += setupFunction; ()
case Cleanup(cleanupFunction) => cleanup += cleanupFunction; ()
case _: Argument => // now handled by whatever constructs `runners`
case Exclude(exclude) => excludeTestsSet ++= exclude; ()
case Listeners(listeners) => testListeners ++= listeners; ()
case Setup(setupFunction, _) => setup += setupFunction; ()
case Cleanup(cleanupFunction, _) => cleanup += cleanupFunction; ()
case _: Argument => // now handled by whatever constructs `runners`
}
}

Expand Down
2 changes: 2 additions & 0 deletions main/src/main/scala/sbt/Defaults.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1425,6 +1425,8 @@ object Defaults extends BuildCommon {
testOptions := Tests.Listeners(testListeners.value) +: (TaskZero / testOptions).value,
testOptionDigests := {
(TaskZero / testOptions).value.flatMap {
case Tests.Setup(_, digest) => Seq(digest)
case Tests.Cleanup(_, digest) => Seq(digest)
case Tests.Argument(fm, args) =>
Seq(
Digest.sha256Hash(
Expand Down
8 changes: 4 additions & 4 deletions sbt-app/src/sbt-test/tests/setup-cleanup/changes/setup.sbt
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
Test / testOptions += {
val baseDir = baseDirectory.value
Tests.Setup { () =>
IO.touch(baseDir / "setup")
IO.touch(baseDir / "setup")
}
}

Test / testOptions += {
val t = baseDirectory.value / "tested"
val c = baseDirectory.value / "cleanup"
Tests.Cleanup { () =>
assert(t.exists, "Didn't exist: " + t.getAbsolutePath)
IO.delete(t)
IO.touch(c)
// assert(t.exists, "Didn't exist: " + t.getAbsolutePath)
IO.delete(t)
IO.touch(c)
}
}
4 changes: 2 additions & 2 deletions sbt-app/src/sbt-test/tests/setup-cleanup/test
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ $ absent tested
$ absent cleanup

# without Setup configured, the setup file won't exist and the test will fail
-> test
-> testQuick

# check that we are starting clean
$ absent setup
Expand Down Expand Up @@ -39,7 +39,7 @@ $ absent tested
$ absent cleanup

# without Setup configured, the setup file won't exist and the test will fail
-> test
-> testQuick

# check that we are starting clean
$ absent setup
Expand Down

0 comments on commit f2cbb7b

Please sign in to comment.