From f22843f91c1a5c1860b20c735c373a18be54940a Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 5 Oct 2017 09:44:05 +0100 Subject: [PATCH 1/5] Move SlashSyntax to the settings component --- .../src/main/scala/sbt/SlashSyntax.scala | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) rename {main => main-settings}/src/main/scala/sbt/SlashSyntax.scala (95%) diff --git a/main/src/main/scala/sbt/SlashSyntax.scala b/main-settings/src/main/scala/sbt/SlashSyntax.scala similarity index 95% rename from main/src/main/scala/sbt/SlashSyntax.scala rename to main-settings/src/main/scala/sbt/SlashSyntax.scala index b614cc3f8..7678907ba 100644 --- a/main/src/main/scala/sbt/SlashSyntax.scala +++ b/main-settings/src/main/scala/sbt/SlashSyntax.scala @@ -34,7 +34,8 @@ trait SlashSyntax { new RichReference(Scope(a, This, This, This)) implicit def sbtSlashSyntaxRichReference(r: Reference): RichReference = Select(r) - implicit def sbtSlashSyntaxRichProject(p: Project): RichReference = (p: Reference) + implicit def sbtSlashSyntaxRichProject[A](p: A)(implicit x: A => Reference): RichReference = + (p: Reference) implicit def sbtSlashSyntaxRichConfigKey(c: ConfigKey): RichConfiguration = new RichConfiguration(Scope(This, Select(c), This, This)) @@ -105,9 +106,7 @@ object SlashSyntax { private[sbt] def materialize: K = key in scope private[sbt] def rescope: TerminalScope = new TerminalScope(scope in key.key) - override def toString: String = { - s"$scope / ${key.key}" - } + override def toString: String = s"$scope / ${key.key}" } } From 97ddc1ffb79540de8ecf4b3b761b18c442dd8abd Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 5 Oct 2017 10:08:13 +0100 Subject: [PATCH 2/5] Copy the non-runtime parts of project/unified to SlashSyntaxTest --- .../src/test/scala/sbt/SlashSyntaxTest.scala | 41 +++++++++++++++++++ sbt/src/main/scala/package.scala | 10 ----- sbt/src/sbt-test/project/unified/build.sbt | 3 +- .../unified/project/Dependencies.scala | 5 --- 4 files changed, 43 insertions(+), 16 deletions(-) create mode 100644 main-settings/src/test/scala/sbt/SlashSyntaxTest.scala delete mode 100644 sbt/src/sbt-test/project/unified/project/Dependencies.scala diff --git a/main-settings/src/test/scala/sbt/SlashSyntaxTest.scala b/main-settings/src/test/scala/sbt/SlashSyntaxTest.scala new file mode 100644 index 000000000..f7a1ae065 --- /dev/null +++ b/main-settings/src/test/scala/sbt/SlashSyntaxTest.scala @@ -0,0 +1,41 @@ +/* + * sbt + * Copyright 2011 - 2017, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under BSD-3-Clause license (see LICENSE) + */ + +package sbt.test + +import sbt.Def.{ Setting, settingKey, taskKey } +import sbt.Scope.Global +import sbt.librarymanagement.ModuleID +import sbt.librarymanagement.syntax._ +import sbt.{ LocalProject, ProjectReference, ThisBuild, Zero } + +object SlashSyntaxTest extends sbt.SlashSyntax { + final case class Proj(id: String) + implicit def projToRef(p: Proj): ProjectReference = LocalProject(p.id) + + val projA = Proj("a") + + val cancelable = settingKey[Boolean]("") + val console = taskKey[Unit]("") + val libraryDependencies = settingKey[Seq[ModuleID]]("") + val name = settingKey[String]("") + val scalaVersion = settingKey[String]("") + val scalacOptions = taskKey[Seq[String]]("") + + val uTest = "com.lihaoyi" %% "utest" % "0.5.3" + + Seq[Setting[_]]( + Global / cancelable := true, + ThisBuild / scalaVersion := "2.12.3", + console / scalacOptions += "-deprecation", + Compile / console / scalacOptions += "-Ywarn-numeric-widen", + projA / Compile / console / scalacOptions += "-feature", + Zero / Zero / name := "foo", + Zero / Zero / Zero / name := "foo", + libraryDependencies += uTest % Test, + ) +} diff --git a/sbt/src/main/scala/package.scala b/sbt/src/main/scala/package.scala index 671f23892..3e067e0de 100644 --- a/sbt/src/main/scala/package.scala +++ b/sbt/src/main/scala/package.scala @@ -43,16 +43,6 @@ package object sbt final val Global = Scope.Global final val GlobalScope = Scope.GlobalScope - // import sbt.{ Configurations => C } - // final val Compile = C.Compile - // final val Test = C.Test - // final val Runtime = C.Runtime - // final val IntegrationTest = C.IntegrationTest - // final val Default = C.Default - // final val Provided = C.Provided - // java.lang.System is more important, so don't alias this one - // final val System = C.System - // final val Optional = C.Optional def config(name: String): Configuration = macro sbt.librarymanagement.ConfigurationMacro.configMacroImpl } diff --git a/sbt/src/sbt-test/project/unified/build.sbt b/sbt/src/sbt-test/project/unified/build.sbt index 7f9835fea..91f772197 100644 --- a/sbt/src/sbt-test/project/unified/build.sbt +++ b/sbt/src/sbt-test/project/unified/build.sbt @@ -1,7 +1,8 @@ -import Dependencies._ import sbt.internal.CommandStrings.{ inspectBrief, inspectDetailed } import sbt.internal.Inspect +val uTest = "com.lihaoyi" %% "utest" % "0.5.3" + lazy val root = (project in file(".")) .settings( Global / cancelable := true, diff --git a/sbt/src/sbt-test/project/unified/project/Dependencies.scala b/sbt/src/sbt-test/project/unified/project/Dependencies.scala deleted file mode 100644 index 0d84ec7d4..000000000 --- a/sbt/src/sbt-test/project/unified/project/Dependencies.scala +++ /dev/null @@ -1,5 +0,0 @@ -import sbt._ - -object Dependencies { - val uTest = "com.lihaoyi" %% "utest" % "0.5.3" -} From db87e4c8717cf4aa868d421be1a9b30b59c4dd74 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 6 Oct 2017 15:05:38 +0100 Subject: [PATCH 3/5] Add SlashSyntaxSpec to validate syntax parity --- .../src/main/scala/sbt/SlashSyntax.scala | 1 + .../src/test/scala/sbt/SlashSyntaxSpec.scala | 238 ++++++++++++++++++ 2 files changed, 239 insertions(+) create mode 100644 main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala diff --git a/main-settings/src/main/scala/sbt/SlashSyntax.scala b/main-settings/src/main/scala/sbt/SlashSyntax.scala index 7678907ba..fc98cfc39 100644 --- a/main-settings/src/main/scala/sbt/SlashSyntax.scala +++ b/main-settings/src/main/scala/sbt/SlashSyntax.scala @@ -58,6 +58,7 @@ object SlashSyntax { /** RichReference wraps a reference to provide the `/` operator for scoping. */ final class RichReference(protected val scope: Scope) extends RichScopeLike { + def /(c: ConfigKey): RichConfiguration = new RichConfiguration(scope in c) def /(c: Configuration): RichConfiguration = new RichConfiguration(scope in c) // This is for handling `Zero / Zero / name`. diff --git a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala new file mode 100644 index 000000000..936a027db --- /dev/null +++ b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala @@ -0,0 +1,238 @@ +/* + * sbt + * Copyright 2011 - 2017, Lightbend, Inc. + * Copyright 2008 - 2010, Mark Harrah + * Licensed under BSD-3-Clause license (see LICENSE) + */ + +package sbt.test + +import org.scalacheck.{ Test => _, _ }, Arbitrary.arbitrary, Gen._, Prop._ + +import java.io.File +import sbt.io.IO +import sbt.SlashSyntax, SlashSyntax.Key +import sbt.{ Scope, ScopeAxis, Scoped, Select, This, Zero }, Scope.{ Global, ThisScope } +import sbt.{ BuildRef, LocalProject, LocalRootProject, ProjectRef, Reference, RootProject, ThisBuild, ThisProject } +import sbt.ConfigKey +import sbt.librarymanagement.syntax._ +import sbt.{ InputKey, SettingKey, TaskKey } +import sbt.internal.util.{ AttributeKey, AttributeMap } + +object BuildDSLInstances { + val genFile: Gen[File] = Gen.oneOf(new File("."), new File("/tmp")) // for now.. + + implicit val arbBuildRef: Arbitrary[BuildRef] = Arbitrary(genFile map (f => BuildRef(IO toURI f))) + + implicit val arbProjectRef: Arbitrary[ProjectRef] = + Arbitrary(for (f <- genFile; id <- Gen.identifier) yield ProjectRef(f, id)) + + implicit val arbLocalProject: Arbitrary[LocalProject] = + Arbitrary(arbitrary[String] map LocalProject) + + implicit val arbRootProject: Arbitrary[RootProject] = Arbitrary(genFile map (RootProject(_))) + + implicit val arbReference: Arbitrary[Reference] = Arbitrary { + Gen.frequency( + 1 -> arbitrary[BuildRef], // 96 + 100 -> ThisBuild, // 10,271 + 3 -> LocalRootProject, // 325 + 23 -> arbitrary[ProjectRef], // 2,283 + 3 -> ThisProject, // 299 + 4 -> arbitrary[LocalProject], // 436 + 11 -> arbitrary[RootProject], // 1,133 + ) + } + + implicit def arbConfigKey: Arbitrary[ConfigKey] = Arbitrary { + Gen.frequency( + 2 -> const[ConfigKey](Compile), + 2 -> const[ConfigKey](Test), + 1 -> const[ConfigKey](Runtime), + 1 -> const[ConfigKey](IntegrationTest), + 1 -> const[ConfigKey](Provided), + ) + } + + implicit def arbAttrKey[A: Manifest]: Arbitrary[AttributeKey[_]] = + Arbitrary(Gen.identifier map (AttributeKey[A](_))) + + implicit def arbInputKey[A: Manifest]: Arbitrary[InputKey[A]] = + Arbitrary(Gen.identifier map (InputKey[A](_))) + + implicit def arbSettingKey[A: Manifest]: Arbitrary[SettingKey[A]] = + Arbitrary(Gen.identifier map (SettingKey[A](_))) + + implicit def arbTaskKey[A: Manifest]: Arbitrary[TaskKey[A]] = + Arbitrary(Gen.identifier map (TaskKey[A](_))) + + implicit def arbKey[A: Manifest]: Arbitrary[Key[_]] = Arbitrary { + Gen.frequency[Key[_]]( + 15 -> arbitrary[InputKey[A]], // 15,431 + 20 -> arbitrary[SettingKey[A]], // 19,645 + 23 -> arbitrary[TaskKey[A]], // 22,867 + ) + } + + implicit def arbScopeAxis[A: Arbitrary]: Arbitrary[ScopeAxis[A]] = + Arbitrary(Gen.oneOf[ScopeAxis[A]](This, Zero, arbitrary[A] map (Select(_)))) + + implicit val arbAttributeMap: Arbitrary[AttributeMap] = Arbitrary { + Gen.frequency( + 20 -> AttributeMap.empty, + 1 -> (for (name <- Gen.identifier; isModule <- arbitrary[Boolean]) + yield AttributeMap.empty + .put(AttributeKey[String]("name"), name) + .put(AttributeKey[Boolean]("isModule"), isModule) + ) + ) + } + + implicit def arbScope: Arbitrary[Scope] = Arbitrary( + for { + r <- arbitrary[ScopeAxis[Reference]] + c <- arbitrary[ScopeAxis[ConfigKey]] + t <- arbitrary[ScopeAxis[AttributeKey[_]]] + e <- arbitrary[ScopeAxis[AttributeMap]] + } yield Scope(r, c, t, e) + ) +} +import BuildDSLInstances._ + +object CustomEquality { + trait Eq[A] { + def equal(x: A, y: A): Boolean + } + + // Avoid reimplementing equality for other standard classes. + trait EqualLowPriority { + implicit def universal[A] = (x: A, y: A) => x == y + } + + object Eq extends EqualLowPriority { + def apply[A: Eq]: Eq[A] = implicitly + + implicit def eqScoped[A <: Scoped]: Eq[A] = (x, y) => x.scope == y.scope && x.key == y.key + } + + implicit class AnyWith_===[A](private val x: A) extends AnyVal { + def ===(y: A)(implicit z: Eq[A]): Boolean = z.equal(x, y) + def =?(y: A)(implicit z: Eq[A]): Prop = { + if (x === y) proved else falsified :| s"Expected $x but got $y" + } + } + + def expectValue[A: Eq](expected: A)(x: A) = x.toString |: (expected =? x) +} +import CustomEquality._ + +object SlashSyntaxSpec extends Properties("SlashSyntax") with SlashSyntax { + property("Global / key == key in Global") = { + def check[K <: Key[K]: Arbitrary] = forAll((k: K) => expectValue(k in Global)(Global / k)) + check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] + } + property("Reference / key == key in Reference") = { + def check[K <: Key[K]: Arbitrary] = forAll((r: Reference, k: K) => expectValue(k in r)(r / k)) + check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] + } + property("Reference / Config / key == key in Reference in Config") = { + def check[K <: Key[K]: Arbitrary] = + forAll((r: Reference, c: ConfigKey, k: K) => expectValue(k in r in c)(r / c / k)) + check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] + } + property("Reference / task / key == key in Reference in task") = { + def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = + forAll((r: Reference, t: K, k: K) => expectValue(k in (r, t))(r / t / k)) + (true + && check[InputKey[String], InputKey[String]] + && check[InputKey[String], SettingKey[String]] + && check[InputKey[String], TaskKey[String]] + && check[SettingKey[String], InputKey[String]] + && check[SettingKey[String], SettingKey[String]] + && check[SettingKey[String], TaskKey[String]] + && check[TaskKey[String], InputKey[String]] + && check[TaskKey[String], SettingKey[String]] + && check[TaskKey[String], TaskKey[String]] + ) + } + property("Reference / Config / task / key == key in Reference in Config in task") = { + def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = + forAll((r: Reference, c: ConfigKey, t: K, k: K) => expectValue(k in (r, c, t))(r / c / t / k)) + (true + && check[InputKey[String], InputKey[String]] + && check[InputKey[String], SettingKey[String]] + && check[InputKey[String], TaskKey[String]] + && check[SettingKey[String], InputKey[String]] + && check[SettingKey[String], SettingKey[String]] + && check[SettingKey[String], TaskKey[String]] + && check[TaskKey[String], InputKey[String]] + && check[TaskKey[String], SettingKey[String]] + && check[TaskKey[String], TaskKey[String]] + ) + } + property("Config / key == key in Config") = { + def check[K <: Key[K]: Arbitrary] = + forAll((c: ConfigKey, k: K) => expectValue(k in c)(c / k)) + check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] + } + property("Config / task / key == key in Config in task") = { + def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = + forAll((c: ConfigKey, t: K, k: K) => expectValue(k in c in t)(c / t / k)) + (true + && check[InputKey[String], InputKey[String]] + && check[InputKey[String], SettingKey[String]] + && check[InputKey[String], TaskKey[String]] + && check[SettingKey[String], InputKey[String]] + && check[SettingKey[String], SettingKey[String]] + && check[SettingKey[String], TaskKey[String]] + && check[TaskKey[String], InputKey[String]] + && check[TaskKey[String], SettingKey[String]] + && check[TaskKey[String], TaskKey[String]] + ) + } + property("task / key == key in task") = { + def check[T <: Key[T]: Arbitrary, K <: Key[K]: Arbitrary] = + forAll((t: K, k: K) => expectValue(k in t)(t / k)) + (true + && check[InputKey[String], InputKey[String]] + && check[InputKey[String], SettingKey[String]] + && check[InputKey[String], TaskKey[String]] + && check[SettingKey[String], InputKey[String]] + && check[SettingKey[String], SettingKey[String]] + && check[SettingKey[String], TaskKey[String]] + && check[TaskKey[String], InputKey[String]] + && check[TaskKey[String], SettingKey[String]] + && check[TaskKey[String], TaskKey[String]] + ) + } + property("Scope / key == key in Scope") = { + def check[K <: Key[K]: Arbitrary] = forAll((s: Scope, k: K) => expectValue(k in s)(s / k)) + check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] + } + property("Reference? / key == key in ThisScope.copy(..)") = { + def check[K <: Key[K]: Arbitrary] = + forAll((r: ScopeAxis[Reference], k: K) => + expectValue(k in ThisScope.copy(project = r))(r / k)) + check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] + } + property("Reference? / ConfigKey? / key == key in ThisScope.copy(..)") = { + def check[K <: Key[K]: Arbitrary] = + forAll((r: ScopeAxis[Reference], c: ScopeAxis[ConfigKey], k: K) => + expectValue(k in ThisScope.copy(project = r, config = c))(r / c / k)) + check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] + } +// property("Reference? / AttributeKey? / key == key in ThisScope.copy(..)") = { +// def check[K <: Key[K]: Arbitrary] = +// forAll( +// (r: ScopeAxis[Reference], t: ScopeAxis[AttributeKey[_]], k: K) => +// expectValue(k in ThisScope.copy(project = r, task = t))(r / t / k)) +// check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] +// } + property("Reference? / ConfigKey? / AttributeKey? / key == key in ThisScope.copy(..)") = { + def check[K <: Key[K]: Arbitrary] = + forAll( + (r: ScopeAxis[Reference], c: ScopeAxis[ConfigKey], t: ScopeAxis[AttributeKey[_]], k: K) => + expectValue(k in ThisScope.copy(project = r, config = c, task = t))(r / c / t / k)) + check[InputKey[String]] && check[SettingKey[String]] && check[TaskKey[String]] + } +} From 9b526f54bf12d6a65dc219de724fd67a08dd5503 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 6 Oct 2017 15:40:31 +0100 Subject: [PATCH 4/5] Rarely include custom scopes in key generators --- .../src/test/scala/sbt/SlashSyntaxSpec.scala | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala index 936a027db..929d48b93 100644 --- a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala +++ b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala @@ -57,14 +57,29 @@ object BuildDSLInstances { implicit def arbAttrKey[A: Manifest]: Arbitrary[AttributeKey[_]] = Arbitrary(Gen.identifier map (AttributeKey[A](_))) - implicit def arbInputKey[A: Manifest]: Arbitrary[InputKey[A]] = - Arbitrary(Gen.identifier map (InputKey[A](_))) + implicit def arbInputKey[A: Manifest]: Arbitrary[InputKey[A]] = Arbitrary { + val keyGen = Gen.identifier map (InputKey[A](_)) + Gen.frequency( + 50 -> keyGen, + 1 -> (for (key <- keyGen; scope <- arbitrary[Scope]) yield key in scope) + ) + } - implicit def arbSettingKey[A: Manifest]: Arbitrary[SettingKey[A]] = - Arbitrary(Gen.identifier map (SettingKey[A](_))) + implicit def arbSettingKey[A: Manifest]: Arbitrary[SettingKey[A]] = Arbitrary { + val keyGen = Gen.identifier map (SettingKey[A](_)) + Gen.frequency( + 50 -> keyGen, + 1 -> (for (key <- keyGen; scope <- arbitrary[Scope]) yield key in scope) + ) + } - implicit def arbTaskKey[A: Manifest]: Arbitrary[TaskKey[A]] = - Arbitrary(Gen.identifier map (TaskKey[A](_))) + implicit def arbTaskKey[A: Manifest]: Arbitrary[TaskKey[A]] = Arbitrary { + val keyGen = Gen.identifier map (TaskKey[A](_)) + Gen.frequency( + 50 -> keyGen, + 1 -> (for (key <- keyGen; scope <- arbitrary[Scope]) yield key in scope) + ) + } implicit def arbKey[A: Manifest]: Arbitrary[Key[_]] = Arbitrary { Gen.frequency[Key[_]]( From 5b03379693d0a9df6dace69d28751383be5ac418 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 6 Oct 2017 17:41:06 +0100 Subject: [PATCH 5/5] Extract withScope --- .../src/test/scala/sbt/SlashSyntaxSpec.scala | 29 +++++++------------ 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala index 929d48b93..8432a3f54 100644 --- a/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala +++ b/main-settings/src/test/scala/sbt/SlashSyntaxSpec.scala @@ -57,29 +57,20 @@ object BuildDSLInstances { implicit def arbAttrKey[A: Manifest]: Arbitrary[AttributeKey[_]] = Arbitrary(Gen.identifier map (AttributeKey[A](_))) - implicit def arbInputKey[A: Manifest]: Arbitrary[InputKey[A]] = Arbitrary { - val keyGen = Gen.identifier map (InputKey[A](_)) - Gen.frequency( + def withScope[K <: Scoped.ScopingSetting[K]](keyGen: Gen[K]): Arbitrary[K] = + Arbitrary(Gen.frequency( 50 -> keyGen, 1 -> (for (key <- keyGen; scope <- arbitrary[Scope]) yield key in scope) - ) - } + )) - implicit def arbSettingKey[A: Manifest]: Arbitrary[SettingKey[A]] = Arbitrary { - val keyGen = Gen.identifier map (SettingKey[A](_)) - Gen.frequency( - 50 -> keyGen, - 1 -> (for (key <- keyGen; scope <- arbitrary[Scope]) yield key in scope) - ) - } + implicit def arbInputKey[A: Manifest]: Arbitrary[InputKey[A]] = + withScope(Gen.identifier map (InputKey[A](_))) - implicit def arbTaskKey[A: Manifest]: Arbitrary[TaskKey[A]] = Arbitrary { - val keyGen = Gen.identifier map (TaskKey[A](_)) - Gen.frequency( - 50 -> keyGen, - 1 -> (for (key <- keyGen; scope <- arbitrary[Scope]) yield key in scope) - ) - } + implicit def arbSettingKey[A: Manifest]: Arbitrary[SettingKey[A]] = + withScope(Gen.identifier map (SettingKey[A](_))) + + implicit def arbTaskKey[A: Manifest]: Arbitrary[TaskKey[A]] = + withScope(Gen.identifier map (TaskKey[A](_))) implicit def arbKey[A: Manifest]: Arbitrary[Key[_]] = Arbitrary { Gen.frequency[Key[_]](