Merge pull request #4499 from dwijnand/ParseKey-rare-failure

Avoid ParseKey failing due to homonymous axes
This commit is contained in:
eugene yokota 2018-12-22 14:29:59 -05:00 committed by GitHub
commit 1e7564c408
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 56 additions and 31 deletions

View File

@ -25,49 +25,66 @@ import sbt.librarymanagement.Configuration
* This includes properly resolving omitted components.
*/
object ParseKey extends Properties("Key parser test") {
property("An explicitly specified axis is always parsed to that explicit value") = forAll {
(skm: StructureKeyMask) =>
import skm.{ structure, key }
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 = resolve(structure, key, mask)
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)}"
propertyWithSeed("An explicitly specified axis is always parsed to that explicit value", None) =
forAll(roundtrip(_))
def roundtrip(skm: StructureKeyMask) = {
import skm.{ structure, key }
// if the configuration axis == Zero
// then a scoped key like `proj/Zero/Zero/name` could render potentially as `Zero/name`
// which would be interpreted as `Zero/Zero/Zero/name` (Global/name)
// so we mitigate this by explicitly displaying the configuration axis set to Zero
val hasZeroConfig = key.scope.config == Zero
val showZeroConfig = hasZeroConfig || hasAmbiguousLowercaseAxes(key)
val mask = if (showZeroConfig) skm.mask.copy(project = true) else skm.mask
val expected = resolve(structure, key, mask)
parseCheck(structure, key, mask, showZeroConfig)(
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)}"
}
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}"
)
propertyWithSeed("An unspecified project axis resolves to the current project", None) = forAll(
noProject(_)
)
def noProject(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
val showZeroConfig = hasAmbiguousLowercaseAxes(key)
parseCheck(structure, key, mask, showZeroConfig)(
sk =>
(hasZeroConfig || sk.scope.project == Select(structure.current))
:| s"Current: ${structure.current}"
)
}
property("An unspecified task axis resolves to Zero") = forAll { (skm: StructureKeyMask) =>
propertyWithSeed("An unspecified task axis resolves to Zero", None) = forAll(noTask(_))
def noTask(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"
) = forAll { (skm: StructureKeyMask) =>
propertyWithSeed(
"An unspecified configuration axis resolves to the first configuration directly defining the key or else Zero",
None
) = forAll(noConfig(_))
def noConfig(skm: StructureKeyMask) = {
import skm.{ structure, key }
val mask = ScopeMask(config = false)
val resolvedConfig = Resolve.resolveConfig(structure.extra, key.key, mask)(key.scope).config
parseCheck(structure, key, mask)(
val showZeroConfig = hasAmbiguousLowercaseAxes(key)
parseCheck(structure, key, mask, showZeroConfig)(
sk => (sk.scope.config == resolvedConfig) || (sk.scope == Scope.GlobalScope)
) :| s"Expected configuration: ${resolvedConfig map (_.name)}"
}
@ -350,4 +367,12 @@ object ParseKey extends Properties("Key parser test") {
case (delegates, key) => Taskk(key, delegates)
}
}
// if both a project and a key share the same name (e.g. "foo")
// then a scoped key like `foo/<conf>/foo/name` would render as `foo/name`
// which would be interpreted as `foo/Zero/Zero/name`
// so we mitigate this by explicitly displaying the configuration axis set to Zero
def hasAmbiguousLowercaseAxes(key: ScopedKey[_]) = PartialFunction.cond(key.scope) {
case Scope(Select(ProjectRef(_, proj)), _, Select(key), _) => proj == key.label
}
}