From 1f14a2c3405682c33b63db67986ed8efe9707232 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sat, 22 Dec 2018 12:48:45 +0000 Subject: [PATCH 1/3] Split out ParseKey properties --- main/src/test/scala/ParseKey.scala | 64 +++++++++++++++++------------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/main/src/test/scala/ParseKey.scala b/main/src/test/scala/ParseKey.scala index 7e348ab73..5ce2bb2cb 100644 --- a/main/src/test/scala/ParseKey.scala +++ b/main/src/test/scala/ParseKey.scala @@ -25,37 +25,43 @@ 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)}" + property("An explicitly specified axis is always parsed to that explicit value") = forAll( + roundtrip(_) + ) + + def roundtrip(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)}" } - 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 project axis resolves to the current project") = 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 + 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) => + property("An unspecified task axis resolves to Zero") = forAll(noTask(_)) + + def noTask(skm: StructureKeyMask) = { import skm.{ structure, key } val mask = skm.mask.copy(task = false) parseCheck(structure, key, mask)(_.scope.task == Zero) @@ -63,7 +69,9 @@ object ParseKey extends Properties("Key parser test") { property( "An unspecified configuration axis resolves to the first configuration directly defining the key or else Zero" - ) = forAll { (skm: StructureKeyMask) => + ) = 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 From 645f484184bcab4969ec41d35f1bbf8fa4bdf0b7 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sat, 22 Dec 2018 12:49:21 +0000 Subject: [PATCH 2/3] Switch ParseKey to propertyWithSeed --- main/src/test/scala/ParseKey.scala | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/main/src/test/scala/ParseKey.scala b/main/src/test/scala/ParseKey.scala index 5ce2bb2cb..428929090 100644 --- a/main/src/test/scala/ParseKey.scala +++ b/main/src/test/scala/ParseKey.scala @@ -25,9 +25,8 @@ 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( - roundtrip(_) - ) + propertyWithSeed("An explicitly specified axis is always parsed to that explicit value", None) = + forAll(roundtrip(_)) def roundtrip(skm: StructureKeyMask) = { import skm.{ structure, key } @@ -45,7 +44,9 @@ object ParseKey extends Properties("Key parser test") { ) :| s"Expected: ${displayFull(expected)}" } - property("An unspecified project axis resolves to the current project") = forAll(noProject(_)) + propertyWithSeed("An unspecified project axis resolves to the current project", None) = forAll( + noProject(_) + ) def noProject(skm: StructureKeyMask) = { import skm.{ structure, key } @@ -59,7 +60,7 @@ object ParseKey extends Properties("Key parser test") { ) } - property("An unspecified task axis resolves to Zero") = forAll(noTask(_)) + propertyWithSeed("An unspecified task axis resolves to Zero", None) = forAll(noTask(_)) def noTask(skm: StructureKeyMask) = { import skm.{ structure, key } @@ -67,8 +68,9 @@ object ParseKey extends Properties("Key parser test") { parseCheck(structure, key, mask)(_.scope.task == Zero) } - property( - "An unspecified configuration axis resolves to the first configuration directly defining the key or else Zero" + propertyWithSeed( + "An unspecified configuration axis resolves to the first configuration directly defining the key or else Zero", + None ) = forAll(noConfig(_)) def noConfig(skm: StructureKeyMask) = { From e533bc93e85c105ebf7647b5597e013fe53e8b82 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sat, 22 Dec 2018 12:50:34 +0000 Subject: [PATCH 3/3] Avoid ParseKey failing due to homonymous axes --- main/src/test/scala/ParseKey.scala | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/main/src/test/scala/ParseKey.scala b/main/src/test/scala/ParseKey.scala index 428929090..953d4bf5d 100644 --- a/main/src/test/scala/ParseKey.scala +++ b/main/src/test/scala/ParseKey.scala @@ -30,13 +30,18 @@ object ParseKey extends Properties("Key parser test") { 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 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 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, hasZeroConfig)( + parseCheck(structure, key, mask, showZeroConfig)( sk => Project.equal(sk, expected, mask) :| s"$sk.key == $expected.key: ${sk.key == expected.key}" @@ -53,7 +58,8 @@ object ParseKey extends Properties("Key parser test") { 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)( + val showZeroConfig = hasAmbiguousLowercaseAxes(key) + parseCheck(structure, key, mask, showZeroConfig)( sk => (hasZeroConfig || sk.scope.project == Select(structure.current)) :| s"Current: ${structure.current}" @@ -77,7 +83,8 @@ object ParseKey extends Properties("Key parser test") { 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)}" } @@ -360,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//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 + } }