sbt/main/src/test/scala/Delegates.scala

180 lines
5.7 KiB
Scala

/*
* sbt
* Copyright 2023, Scala center
* Copyright 2011 - 2022, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt
import sbt.internal.util.Types.idFun
import sbt.internal.TestBuild._
import hedgehog._
import hedgehog.Result.{ all, assert, failure, success }
import hedgehog.runner._
object Delegates extends Properties {
override def tests: List[Test] =
List(
property(
"generate non-empty configs",
cGen.forAll.map { c =>
assert(c.nonEmpty)
}
),
property(
"generate non-empty tasks",
tGen.forAll.map { t =>
assert(t.nonEmpty)
}
),
property(
"no duplicate scopes",
keysGen.forAll.map { keys =>
allDelegates(keys) { (_, ds) =>
ds.distinct.size ==== ds.size
}
}
),
property(
"delegates non-empty",
keysGen.forAll.map { keys =>
allDelegates(keys) { (_, ds) =>
assert(ds.nonEmpty)
}
}
),
property("An initially Zero axis is Zero in all delegates", allAxes(alwaysZero)),
property(
"Projects precede builds precede Zero",
keysGen.forAll.map { keys =>
allDelegates(keys) { (scope, ds) =>
val projectAxes = ds.map(_.project)
val nonProject = projectAxes.dropWhile {
case Select(_: ProjectRef) => true; case _ => false
}
val global = nonProject.dropWhile { case Select(_: BuildRef) => true; case _ => false }
all(global.map { _ ==== Zero }.toList)
}
}
),
property(
"Initial scope present with all combinations of Global axes",
allAxes((s, ds, _) => globalCombinations(s, ds))
),
property(
"initial scope first",
keysGen.forAll.map { keys =>
allDelegates(keys) { (scope, ds) =>
ds.head ==== scope
}
}
),
property(
"global scope last",
keysGen.forAll.map { keys =>
allDelegates(keys) { (_, ds) =>
ds.last ==== Scope.GlobalScope
}
}
),
property(
"Project axis delegates to BuildRef then Zero",
keysGen.forAll.map { keys =>
allDelegates(keys) { (key, ds) =>
key.project match {
case Zero => success // filtering out of testing
case Select(rr: ResolvedReference) =>
rr match {
case BuildRef(_) =>
assert(ds.indexOf(key) < ds.indexOf(key.copy(project = Zero)))
case ProjectRef(uri, _) =>
val buildScoped = key.copy(project = Select(BuildRef(uri)))
val idxKey = ds.indexOf(key)
val idxB = ds.indexOf(buildScoped)
val z = key.copy(project = Zero)
val idxZ = ds.indexOf(z)
(z ==== Scope.GlobalScope)
.or(
assert((idxKey < idxB) && (idxB < idxZ))
.log(s"idxKey = $idxKey; idxB = $idxB; idxZ = $idxZ")
)
}
case Select(_) | This =>
failure.log(s"Scope's reference should be resolved, but was ${key.project}")
}
}
}
),
property(
"Config axis delegates to parent configuration",
keysGen.forAll.map { keys =>
allDelegates(keys) { (key, ds) =>
key.config match {
case Zero => success
case Select(config) =>
key.project match {
case Select(p @ ProjectRef(_, _)) =>
val r = keys.env.resolve(p)
keys.env.inheritConfig(r, config).headOption.fold(success) { parent =>
val idxKey = ds.indexOf(key)
val a = key.copy(config = Select(parent))
val idxA = ds.indexOf(a)
assert(idxKey < idxA)
.log(s"idxKey = $idxKey; a = $a; idxA = $idxA")
}
case _ => success
}
case _ => success
}
}
}
)
)
def allAxes(f: (Scope, Seq[Scope], Scope => ScopeAxis[_]) => hedgehog.Result): Property =
keysGen.forAll.map { keys =>
allDelegates(keys) { (s, ds) =>
all(List(f(s, ds, _.project), f(s, ds, _.config), f(s, ds, _.task), f(s, ds, _.extra)))
}
}
def allDelegates(keys: TestKeys)(f: (Scope, Seq[Scope]) => hedgehog.Result): hedgehog.Result =
all(keys.scopes.map { scope =>
val delegates = keys.env.delegates(scope)
f(scope, delegates)
.log("Scope: " + Scope.display(scope, "_"))
.log("Delegates:\n\t" + delegates.map(scope => Scope.display(scope, "_")).mkString("\n\t"))
}.toList)
def alwaysZero(s: Scope, ds: Seq[Scope], axis: Scope => ScopeAxis[_]): hedgehog.Result =
assert(axis(s) != Zero).or(
all(ds.map { d =>
axis(d) ==== Zero
}.toList)
)
def globalCombinations(s: Scope, ds: Seq[Scope]): hedgehog.Result = {
val mods = List[Scope => Scope](
_.copy(project = Zero),
_.copy(config = Zero),
_.copy(task = Zero),
_.copy(extra = Zero),
)
val modAndIdent = mods.map(_ :: idFun[Scope] :: Nil)
def loop(cur: Scope, acc: List[Scope], rem: List[Seq[Scope => Scope]]): Seq[Scope] =
rem match {
case Nil => acc
case x :: xs =>
x flatMap { mod =>
val s = mod(cur)
loop(s, s :: acc, xs)
}
}
all(loop(s, Nil, modAndIdent).map(x => assert(ds contains x)).toList)
}
}