Skip to content

Commit

Permalink
Merge pull request #221 from dwijnand/cleanup/VersionNumberSpec
Browse files Browse the repository at this point in the history
Cleanup VersionNumberSpec
  • Loading branch information
dwijnand committed Mar 20, 2018
2 parents 6feacbd + 4b9eb38 commit c6b2b62
Showing 1 changed file with 184 additions and 136 deletions.
320 changes: 184 additions & 136 deletions core/src/test/scala/sbt/librarymanagement/VersionNumberSpec.scala
Original file line number Diff line number Diff line change
@@ -1,150 +1,198 @@
package sbt.librarymanagement

import sbt.internal.librarymanagement.UnitSpec

// This is a specification to check the version number parsing.
class VersionNumberSpec extends UnitSpec {
"1" should "be parsed" in beParsedAs("1", Seq(1), Seq(), Seq())
it should "breakdown" in breakDownTo("1", Some(1))
it should "cascade" in generateCorrectCascadingNumbers("1", Seq("1"))

"1.0" should "be parsed" in beParsedAs("1.0", Seq(1, 0), Seq(), Seq())
it should "breakdown" in breakDownTo("1.0", Some(1), Some(0))
it should "cascade" in generateCorrectCascadingNumbers("1.0", Seq("1.0"))

"1.0.0" should "be parsed" in beParsedAs("1.0.0", Seq(1, 0, 0), Seq(), Seq())
it should "breakdown" in breakDownTo("1.0.0", Some(1), Some(0), Some(0))
it should "cascade" in generateCorrectCascadingNumbers("1.0.0", Seq("1.0.0", "1.0"))
it should "be SemVer compat with 1.0.1" in beSemVerCompatWith("1.0.0", "1.0.1")
it should "be SemVer compat with 1.1.1" in beSemVerCompatWith("1.0.0", "1.1.1")
it should "not be SemVer compat with 2.0.0" in notBeSemVerCompatWith("1.0.0", "2.0.0")
it should "not be SemVer compat with 1.0.0-M1" in notBeSemVerCompatWith("1.0.0", "1.0.0-M1")
it should "be SecSeg compat with 1.0.1" in beSecSegCompatWith("1.0.0", "1.0.1")
it should "not be SecSeg compat with 1.1.1" in notBeSecSegCompatWith("1.0.0", "1.1.1")
it should "not be SecSeg compat with 2.0.0" in notBeSecSegCompatWith("1.0.0", "2.0.0")
it should "not be SecSeg compat with 1.0.0-M1" in notBeSecSegCompatWith("1.0.0", "1.0.0-M1")

"1.0.0.0" should "be parsed" in beParsedAs("1.0.0.0", Seq(1, 0, 0, 0), Seq(), Seq())
it should "breakdown" in breakDownTo("1.0.0.0", Some(1), Some(0), Some(0), Some(0))
it should "cascade" in generateCorrectCascadingNumbers("1.0.0.0", Seq("1.0.0.0", "1.0.0", "1.0"))

"0.12.0" should "be parsed" in beParsedAs("0.12.0", Seq(0, 12, 0), Seq(), Seq())
it should "breakdown" in breakDownTo("0.12.0", Some(0), Some(12), Some(0))
it should "cascade" in generateCorrectCascadingNumbers("0.12.0", Seq("0.12.0", "0.12"))
it should "not be SemVer compat with 0.12.0-RC1" in notBeSemVerCompatWith("0.12.0", "0.12.0-RC1")
it should "not be SemVer compat with 0.12.1" in notBeSemVerCompatWith("0.12.0", "0.12.1")
it should "not be SemVer compat with 0.12.1-M1" in notBeSemVerCompatWith("0.12.0", "0.12.1-M1")
it should "not be SecSeg compat with 0.12.0-RC1" in notBeSecSegCompatWith("0.12.0", "0.12.0-RC1")
it should "be SecSeg compat with 0.12.1" in beSecSegCompatWith("0.12.0", "0.12.1")
it should "be SecSeg compat with 0.12.1-M1" in beSecSegCompatWith("0.12.0", "0.12.1-M1")

"0.1.0-SNAPSHOT" should "be parsed" in beParsedAs("0.1.0-SNAPSHOT",
Seq(0, 1, 0),
Seq("SNAPSHOT"),
Seq())
it should "cascade" in generateCorrectCascadingNumbers("0.1.0-SNAPSHOT",
Seq("0.1.0-SNAPSHOT", "0.1.0", "0.1"))
it should "be SemVer compat with 0.1.0-SNAPSHOT" in beSemVerCompatWith("0.1.0-SNAPSHOT",
"0.1.0-SNAPSHOT")
it should "not be SemVer compat with 0.1.0" in notBeSemVerCompatWith("0.1.0-SNAPSHOT", "0.1.0")
it should "be SemVer compat with 0.1.0-SNAPSHOT+001" in beSemVerCompatWith("0.1.0-SNAPSHOT",
"0.1.0-SNAPSHOT+001")
it should "be SecSeg compat with 0.1.0-SNAPSHOT" in beSecSegCompatWith("0.1.0-SNAPSHOT",
"0.1.0-SNAPSHOT")
it should "be not SecSeg compat with 0.1.0" in notBeSecSegCompatWith("0.1.0-SNAPSHOT", "0.1.0")
it should "be SecSeg compat with 0.1.0-SNAPSHOT+001" in beSecSegCompatWith("0.1.0-SNAPSHOT",
"0.1.0-SNAPSHOT+001")

"0.1.0-M1" should "be parsed" in beParsedAs("0.1.0-M1", Seq(0, 1, 0), Seq("M1"), Seq())
it should "cascade" in generateCorrectCascadingNumbers("0.1.0-M1",
Seq("0.1.0-M1", "0.1.0", "0.1"))

"0.1.0-RC1" should "be parsed" in beParsedAs("0.1.0-RC1", Seq(0, 1, 0), Seq("RC1"), Seq())
it should "cascade" in generateCorrectCascadingNumbers("0.1.0-RC1",
Seq("0.1.0-RC1", "0.1.0", "0.1"))

"0.1.0-MSERVER-1" should "be parsed" in beParsedAs("0.1.0-MSERVER-1",
Seq(0, 1, 0),
Seq("MSERVER", "1"),
Seq())
it should "cascade" in generateCorrectCascadingNumbers("0.1.0-MSERVER-1",
Seq("0.1.0-MSERVER-1", "0.1.0", "0.1"))

"2.10.4-20140115-000117-b3a-sources" should "be parsed" in {
beParsedAs("2.10.4-20140115-000117-b3a-sources",
Seq(2, 10, 4),
Seq("20140115", "000117", "b3a", "sources"),
Seq())
}
it should "cascade" in generateCorrectCascadingNumbers(
"2.10.4-20140115-000117-b3a-sources",
Seq("2.10.4-20140115-000117-b3a-sources", "2.10.4", "2.10"))
it should "be SemVer compat with 2.0.0" in beSemVerCompatWith(
"2.10.4-20140115-000117-b3a-sources",
"2.0.0")
it should "be not SecSeg compat with 2.0.0" in notBeSecSegCompatWith(
"2.10.4-20140115-000117-b3a-sources",
"2.0.0")

"20140115000117-b3a-sources" should "be parsed" in {
beParsedAs("20140115000117-b3a-sources", Seq(20140115000117L), Seq("b3a", "sources"), Seq())
}
it should "cascade" in generateCorrectCascadingNumbers("20140115000117-b3a-sources",
Seq("20140115000117-b3a-sources"))

"1.0.0-alpha+001+002" should "be parsed" in {
beParsedAs("1.0.0-alpha+001+002", Seq(1, 0, 0), Seq("alpha"), Seq("+001", "+002"))
}
it should "cascade" in generateCorrectCascadingNumbers(
"1.0.0-alpha+001+002",
Seq("1.0.0-alpha+001+002", "1.0.0", "1.0")
)

"non.space.!?string" should "be parsed" in {
beParsedAs("non.space.!?string", Seq(), Seq(), Seq("non.space.!?string"))
}
it should "cascade" in generateCorrectCascadingNumbers("non.space.!?string",
Seq("non.space.!?string"))

"space !?string" should "be parsed as an error" in beParsedAsError("space !?string")
"blank string" should "be parsed as an error" in beParsedAsError("")

def beParsedAs(s: String, ns: Seq[Long], ts: Seq[String], es: Seq[String]) =
s match {
case VersionNumber(ns1, ts1, es1) if (ns1 == ns && ts1 == ts && es1 == es) =>
(VersionNumber(ns, ts, es).toString shouldBe s)
(VersionNumber(ns, ts, es) shouldBe VersionNumber(ns, ts, es))
import org.scalatest.{ FreeSpec, Inside, Matchers }

// This is a specification to check VersionNumber and VersionNumberCompatibility.
class VersionNumberSpec extends FreeSpec with Matchers with Inside {
import VersionNumber.{ SemVer, SecondSegment }

version("1") { v =>
assertParsesTo(v, Seq(1), Seq(), Seq())
assertBreaksDownTo(v, Some(1))
assertCascadesTo(v, Seq("1"))
}

version("1.0") { v =>
assertParsesTo(v, Seq(1, 0), Seq(), Seq())
assertBreaksDownTo(v, Some(1), Some(0))
assertCascadesTo(v, Seq("1.0"))
}

version("1.0.0") { v =>
assertParsesTo(v, Seq(1, 0, 0), Seq(), Seq())
assertBreaksDownTo(v, Some(1), Some(0), Some(0))
assertCascadesTo(v, Seq("1.0.0", "1.0"))

assertIsCompatibleWith(v, "1.0.1", SemVer)
assertIsCompatibleWith(v, "1.1.1", SemVer)
assertIsNotCompatibleWith(v, "2.0.0", SemVer)
assertIsNotCompatibleWith(v, "1.0.0-M1", SemVer)

assertIsCompatibleWith(v, "1.0.1", SecondSegment)
assertIsNotCompatibleWith(v, "1.1.1", SecondSegment)
assertIsNotCompatibleWith(v, "2.0.0", SecondSegment)
assertIsNotCompatibleWith(v, "1.0.0-M1", SecondSegment)
}

version("1.0.0.0") { v =>
assertParsesTo(v, Seq(1, 0, 0, 0), Seq(), Seq())
assertBreaksDownTo(v, Some(1), Some(0), Some(0), Some(0))
assertCascadesTo(v, Seq("1.0.0.0", "1.0.0", "1.0"))
}

version("0.12.0") { v =>
assertParsesTo(v, Seq(0, 12, 0), Seq(), Seq())
assertBreaksDownTo(v, Some(0), Some(12), Some(0))
assertCascadesTo(v, Seq("0.12.0", "0.12"))

assertIsNotCompatibleWith(v, "0.12.0-RC1", SemVer)
assertIsNotCompatibleWith(v, "0.12.1", SemVer)
assertIsNotCompatibleWith(v, "0.12.1-M1", SemVer)

assertIsNotCompatibleWith(v, "0.12.0-RC1", SecondSegment)
assertIsCompatibleWith(v, "0.12.1", SecondSegment)
assertIsCompatibleWith(v, "0.12.1-M1", SecondSegment)
}

version("0.1.0-SNAPSHOT") { v =>
assertParsesTo(v, Seq(0, 1, 0), Seq("SNAPSHOT"), Seq())
assertCascadesTo(v, Seq("0.1.0-SNAPSHOT", "0.1.0", "0.1"))

assertIsCompatibleWith(v, "0.1.0-SNAPSHOT", SemVer)
assertIsNotCompatibleWith(v, "0.1.0", SemVer)
assertIsCompatibleWith(v, "0.1.0-SNAPSHOT+001", SemVer)

assertIsCompatibleWith(v, "0.1.0-SNAPSHOT", SecondSegment)
assertIsNotCompatibleWith(v, "0.1.0", SecondSegment)
assertIsCompatibleWith(v, "0.1.0-SNAPSHOT+001", SecondSegment)
}

version("0.1.0-M1") { v =>
assertParsesTo(v, Seq(0, 1, 0), Seq("M1"), Seq())
assertCascadesTo(v, Seq("0.1.0-M1", "0.1.0", "0.1"))
}

version("0.1.0-RC1") { v =>
assertParsesTo(v, Seq(0, 1, 0), Seq("RC1"), Seq())
assertCascadesTo(v, Seq("0.1.0-RC1", "0.1.0", "0.1"))
}

version("0.1.0-MSERVER-1") { v =>
assertParsesTo(v, Seq(0, 1, 0), Seq("MSERVER", "1"), Seq())
assertCascadesTo(v, Seq("0.1.0-MSERVER-1", "0.1.0", "0.1"))
}

version("2.10.4-20140115-000117-b3a-sources") { v =>
assertParsesTo(v, Seq(2, 10, 4), Seq("20140115", "000117", "b3a", "sources"), Seq())
assertCascadesTo(v, Seq("2.10.4-20140115-000117-b3a-sources", "2.10.4", "2.10"))
assertIsCompatibleWith(v, "2.0.0", SemVer)
assertIsNotCompatibleWith(v, "2.0.0", SecondSegment)
}

version("20140115000117-b3a-sources") { v =>
assertParsesTo(v, Seq(20140115000117L), Seq("b3a", "sources"), Seq())
assertCascadesTo(v, Seq("20140115000117-b3a-sources"))
}

version("1.0.0-alpha+001+002") { v =>
assertParsesTo(v, Seq(1, 0, 0), Seq("alpha"), Seq("+001", "+002"))
assertCascadesTo(v, Seq("1.0.0-alpha+001+002", "1.0.0", "1.0"))
}

version("non.space.!?string") { v =>
assertParsesTo(v, Seq(), Seq(), Seq("non.space.!?string"))
assertCascadesTo(v, Seq("non.space.!?string"))
}

version("space !?string") { v =>
assertParsesToError(v)
}
version("") { v =>
assertParsesToError(v)
}

////

private[this] final class VersionString(val value: String)

private[this] def version(s: String)(f: VersionString => Unit) =
s"""Version "$s"""" - {
f(new VersionString(s))
}

private[this] def assertParsesTo(
v: VersionString,
ns: Seq[Long],
ts: Seq[String],
es: Seq[String]
): Unit =
s"should parse to ($ns, $ts, $es)" in inside(v.value) {
case VersionNumber(ns1, ts1, es1) =>
sys.error(s"$ns1, $ts1, $es1")
(ns1 shouldBe ns)
(ts1 shouldBe ts)
(es1 shouldBe es)
(VersionNumber(ns, ts, es).toString shouldBe v.value)
(VersionNumber(ns, ts, es) shouldBe VersionNumber(ns, ts, es))
}

private[this] def assertParsesToError(v: VersionString): Unit =
"should parse as an error" in {
v.value should not matchPattern {
case s: String if VersionNumber.unapply(s).isDefined => // because of unapply overloading
}
}
def breakDownTo(s: String,
major: Option[Long],
minor: Option[Long] = None,
patch: Option[Long] = None,
buildNumber: Option[Long] = None) =
s match {

private[this] def assertBreaksDownTo(
v: VersionString,
major: Option[Long],
minor: Option[Long] = None,
patch: Option[Long] = None,
buildNumber: Option[Long] = None
): Unit =
s"should breakdown to ($major, $minor, $patch, $buildNumber)" in inside(v.value) {
case VersionNumber(ns, ts, es) =>
val v = VersionNumber(ns, ts, es)
(v._1 shouldBe major)
(v._2 shouldBe minor)
(v._3 shouldBe patch)
(v._4 shouldBe buildNumber)
}
def beParsedAsError(s: String): Unit =
s match {
case VersionNumber(_, _, _) => sys.error(s)
case _ => ()

private[this] def assertCascadesTo(v: VersionString, ns: Seq[String]): Unit = {
s"should cascade to $ns" in {
val versionNumbers = ns.toVector map VersionNumber.apply
VersionNumber(v.value).cascadingVersions shouldBe versionNumbers
}
}

private[this] def assertIsCompatibleWith(
v1: VersionString,
v2: String,
vnc: VersionNumberCompatibility
): Unit =
checkCompat(true, vnc, v1, v2)

private[this] def assertIsNotCompatibleWith(
v1: VersionString,
v2: String,
vnc: VersionNumberCompatibility
): Unit =
checkCompat(false, vnc, v1, v2)

private[this] def checkCompat(
expectOutcome: Boolean,
vnc: VersionNumberCompatibility,
v1: VersionString,
v2: String
) = {
val prefix = if (expectOutcome) "should" else "should NOT"
val compatibilityStrategy = vnc match {
case SemVer => "SemVer"
case SecondSegment => "SecondSegment"
case _ => val s = vnc.name; if (s contains " ") s""""$s"""" else s
}
s"$prefix be $compatibilityStrategy compatible with $v2" in {
vnc.isCompatible(VersionNumber(v1.value), VersionNumber(v2)) shouldBe expectOutcome
}
def beSemVerCompatWith(v1: String, v2: String) =
VersionNumber.SemVer.isCompatible(VersionNumber(v1), VersionNumber(v2)) shouldBe true
def notBeSemVerCompatWith(v1: String, v2: String) =
VersionNumber.SemVer.isCompatible(VersionNumber(v1), VersionNumber(v2)) shouldBe false
def beSecSegCompatWith(v1: String, v2: String) =
VersionNumber.SecondSegment.isCompatible(VersionNumber(v1), VersionNumber(v2)) shouldBe true
def notBeSecSegCompatWith(v1: String, v2: String) =
VersionNumber.SecondSegment.isCompatible(VersionNumber(v1), VersionNumber(v2)) shouldBe false
def generateCorrectCascadingNumbers(s: String, ns: Seq[String]) = {
val versionNumbers = ns.toVector map VersionNumber.apply
VersionNumber(s).cascadingVersions shouldBe versionNumbers
}
}

0 comments on commit c6b2b62

Please sign in to comment.