Cleanup test/ParseKey

This commit is contained in:
Dale Wijnand 2018-03-26 23:57:34 +01:00
parent ca874d5d5f
commit 25988d2256
No known key found for this signature in database
GPG Key ID: 4F256E3D151DF5EF
1 changed files with 82 additions and 134 deletions

View File

@ -8,166 +8,114 @@
package sbt
import Def.{ displayFull, displayMasked, ScopedKey }
import sbt.internal.{ TestBuild, Resolve }
import TestBuild._
import sbt.internal.util.complete._
import sbt.internal.{ TestBuild, Resolve }, TestBuild._
import sbt.internal.util.complete.Parser
import org.scalacheck._
import Gen._
import Prop._
import Arbitrary.arbBool
import org.scalacheck._, Arbitrary.arbitrary, Gen._, Prop._
/**
* Tests that the scoped key parser in Act can correctly parse a ScopedKey converted by Def.show*Key.
* This includes properly resolving omitted components.
*/
object ParseKey extends Properties("Key parser test") {
final val MaxKeys = 5
final val MaxScopedKeys = 100
implicit val gstructure = genStructure
property("An explicitly specified axis is always parsed to that explicit value") =
forAllNoShrink(structureDefinedKey) { (skm: StructureKeyMask) =>
import skm.{ structure, key, mask => mask0 }
val hasZeroConfig = key.scope.config == Zero
val mask = if (hasZeroConfig) mask0.copy(project = true) else mask0
val expected = resolve(structure, key, mask)
// Note that this explicitly displays the configuration axis set to Zero.
// This is to disambiguate `proj/Zero/name`, which could render potentially
// as `Zero/name`, but could be interpretted as `Zero/Zero/name`.
val s = displayMasked(key, mask, hasZeroConfig)
("Key: " + displayPedantic(key)) |:
parseExpected(structure, s, expected, mask)
}
property("An unspecified project axis resolves to the current project") =
forAllNoShrink(structureDefinedKey) { (skm: StructureKeyMask) =>
import skm.{ structure, key }
val mask = skm.mask.copy(project = false)
val string = displayMasked(key, mask)
// skip when config axis is set to Zero
val hasZeroConfig = key.scope.config == Zero
("Key: " + displayPedantic(key)) |:
("Mask: " + mask) |:
("Current: " + structure.current) |:
parse(structure, string) {
case Left(_) => false
case Right(_) if hasZeroConfig => true
case Right(sk) => sk.scope.project == Select(structure.current)
}
}
property("An unspecified task axis resolves to Zero") = forAllNoShrink(structureDefinedKey) {
property("An explicitly specified axis is always parsed to that explicit value") = forAll {
(skm: StructureKeyMask) =>
import skm.{ structure, key }
val mask = skm.mask.copy(task = false)
val string = displayMasked(key, mask)
val hasZeroConfig = key.scope.config == Zero
val mask = if (hasZeroConfig) skm.mask.copy(project = true) else skm.mask
// Note that this explicitly displays the configuration axis set to Zero.
// This is to disambiguate `proj/Zero/name`, which could render potentially
// as `Zero/name`, but could be interpreted as `Zero/Zero/name`.
val expected = ScopedKey(
Resolve(structure.extra, Select(structure.current), key.key, mask)(key.scope),
key.key
)
parseCheck(structure, key, mask, hasZeroConfig)(
sk =>
Project.equal(sk, expected, mask)
:| s"$sk.key == $expected.key: ${sk.key == expected.key}"
:| s"${sk.scope} == ${expected.scope}: ${Scope.equal(sk.scope, expected.scope, mask)}"
) :| s"Expected: ${displayFull(expected)}"
}
("Key: " + displayPedantic(key)) |:
("Mask: " + mask) |:
parse(structure, string) {
case Left(_) => false
case Right(sk) => sk.scope.task == Zero
}
property("An unspecified project axis resolves to the current project") = forAll {
(skm: StructureKeyMask) =>
import skm.{ structure, key }
val mask = skm.mask.copy(project = false)
// skip when config axis is set to Zero
val hasZeroConfig = key.scope.config == Zero
parseCheck(structure, key, mask)(
sk =>
(hasZeroConfig || sk.scope.project == Select(structure.current))
:| s"Current: ${structure.current}"
)
}
property("An unspecified task axis resolves to Zero") = forAll { (skm: StructureKeyMask) =>
import skm.{ structure, key }
val mask = skm.mask.copy(task = false)
parseCheck(structure, key, mask)(_.scope.task == Zero)
}
property(
"An unspecified configuration axis resolves to the first configuration directly defining the key or else Zero") =
forAllNoShrink(structureDefinedKey) { (skm: StructureKeyMask) =>
forAll { (skm: StructureKeyMask) =>
import skm.{ structure, key }
val mask = ScopeMask(config = false)
val string = displayMasked(key, mask)
val resolvedConfig = Resolve.resolveConfig(structure.extra, key.key, mask)(key.scope).config
("Key: " + displayPedantic(key)) |:
("Mask: " + mask) |:
("Expected configuration: " + resolvedConfig.map(_.name)) |:
parse(structure, string) {
case Right(sk) => (sk.scope.config == resolvedConfig) || (sk.scope == Scope.GlobalScope)
case Left(_) => false
}
parseCheck(structure, key, mask)(
sk => (sk.scope.config == resolvedConfig) || (sk.scope == Scope.GlobalScope)
) :| s"Expected configuration: ${resolvedConfig map (_.name)}"
}
def displayPedantic(scoped: ScopedKey[_]): String =
Scope.displayPedantic(scoped.scope, scoped.key.label)
lazy val structureDefinedKey: Gen[StructureKeyMask] = structureKeyMask { s =>
for (scope <- TestBuild.scope(s.env); key <- oneOf(s.allAttributeKeys.toSeq))
yield ScopedKey(scope, key)
}
def structureKeyMask(genKey: Structure => Gen[ScopedKey[_]])(
implicit maskGen: Gen[ScopeMask],
structureGen: Gen[Structure]): Gen[StructureKeyMask] =
for (mask <- maskGen; structure <- structureGen; key <- genKey(structure))
yield new StructureKeyMask(structure, key, mask)
final class StructureKeyMask(val structure: Structure, val key: ScopedKey[_], val mask: ScopeMask)
def resolve(structure: Structure, key: ScopedKey[_], mask: ScopeMask): ScopedKey[_] =
ScopedKey(Resolve(structure.extra, Select(structure.current), key.key, mask)(key.scope),
key.key)
def parseExpected(structure: Structure,
s: String,
expected: ScopedKey[_],
mask: ScopeMask): Prop =
("Expected: " + displayFull(expected)) |:
("Mask: " + mask) |:
parse(structure, s) {
case Left(_) => false
case Right(sk) =>
(s"${sk}.key == ${expected}.key: ${sk.key == expected.key}") |:
(s"${sk.scope} == ${expected.scope}: ${Scope.equal(sk.scope, expected.scope, mask)}") |:
Project.equal(sk, expected, mask)
}
def parse(structure: Structure, s: String)(f: Either[String, ScopedKey[_]] => Prop): Prop = {
val parser = makeParser(structure)
val parsed = DefaultParsers.result(parser, s).left.map(_().toString)
val showParsed = parsed.right.map(displayFull)
("Key string: '" + s + "'") |:
("Parsed: " + showParsed) |:
("Structure: " + structure) |:
f(parsed)
}
// Here we're shadowing the in-scope implicit called `mkEnv` for this method
// so that it will use the passed-in `Gen` rather than the one imported
// from TestBuild.
def genStructure(implicit mkEnv: Gen[Env]): Gen[Structure] =
structureGenF { (scopes: Seq[Scope], env: Env, current: ProjectRef) =>
val settings =
for {
scope <- scopes
t <- env.tasks
} yield Def.setting(ScopedKey(scope, t.key), Def.value(""))
TestBuild.structure(env, settings, current)
}
// Here we're shadowing the in-scope implicit called `mkEnv` for this method
// so that it will use the passed-in `Gen` rather than the one imported
// from TestBuild.
def structureGenF(f: (Seq[Scope], Env, ProjectRef) => Structure)(
implicit mkEnv: Gen[Env]): Gen[Structure] =
structureGen((s, e, p) => Gen.const(f(s, e, p)))
// Here we're shadowing the in-scope implicit called `mkEnv` for this method
// so that it will use the passed-in `Gen` rather than the one imported
// from TestBuild.
def structureGen(f: (Seq[Scope], Env, ProjectRef) => Gen[Structure])(
implicit mkEnv: Gen[Env]): Gen[Structure] =
implicit val arbStructure: Arbitrary[Structure] = Arbitrary {
for {
env <- mkEnv
loadFactor <- choose(0.0, 1.0)
scopes <- pickN(loadFactor, env.allFullScopes)
current <- oneOf(env.allProjects.unzip._1)
structure <- f(scopes, env, current)
structure <- {
val settings = for (scope <- scopes; t <- env.tasks)
yield Def.setting(ScopedKey(scope, t.key), Def.value(""))
TestBuild.structure(env, settings, current)
}
} yield structure
}
// pickN is a function that randomly picks load % items from the from sequence.
final class StructureKeyMask(val structure: Structure, val key: ScopedKey[_], val mask: ScopeMask)
implicit val arbStructureKeyMask: Arbitrary[StructureKeyMask] = Arbitrary {
for {
mask <- maskGen
structure <- arbitrary[Structure]
key <- for {
scope <- TestBuild.scope(structure.env)
key <- oneOf(structure.allAttributeKeys.toSeq)
} yield ScopedKey(scope, key)
} yield new StructureKeyMask(structure, key, mask)
}
def parseCheck(
structure: Structure,
key: ScopedKey[_],
mask: ScopeMask,
showZeroConfig: Boolean = false,
)(f: ScopedKey[_] => Prop): Prop = {
val s = displayMasked(key, mask, showZeroConfig)
val parser = makeParser(structure)
val parsed = Parser.result(parser, s).left.map(_().toString)
(
parsed.fold(_ => falsified, f)
:| s"Key: ${Scope.displayPedantic(key.scope, key.key.label)}"
:| s"Mask: $mask"
:| s"Key string: '$s'"
:| s"Parsed: ${parsed.right.map(displayFull)}"
:| s"Structure: $structure"
)
}
// pickN is a function that randomly picks load % items from the "from" sequence.
// The rest of the tests expect at least one item, so I changed it to return 1 in case of 0.
def pickN[T](load: Double, from: Seq[T]): Gen[Seq[T]] =
pick(Math.max((load * from.size).toInt, 1), from)
pick((load * from.size).toInt max 1, from)
}