Merge pull request #507 from coursier/topic/foo

Various things
This commit is contained in:
Alexandre Archambault 2017-04-22 14:27:42 +02:00 committed by GitHub
commit 3c46f452eb
22 changed files with 416 additions and 160 deletions

View File

@ -189,44 +189,78 @@ object Cache {
} finally if (out != null) out.close()
}
private def defaultRetryCount = 3
private lazy val retryCount =
sys.props
.get("coursier.sslexception-retry")
.flatMap(s => scala.util.Try(s.toInt).toOption)
.filter(_ >= 0)
.getOrElse(defaultRetryCount)
private def downloading[T](
url: String,
file: File,
logger: Option[Logger]
logger: Option[Logger],
retry: Int = retryCount
)(
f: => FileError \/ T
): FileError \/ T =
try {
val o = new Object
val prev = urlLocks.putIfAbsent(url, o)
if (prev == null) {
logger.foreach(_.downloadingArtifact(url, file))
): FileError \/ T = {
val res =
try \/-(f)
catch {
case nfe: FileNotFoundException if nfe.getMessage != null =>
logger.foreach(_.downloadedArtifact(url, success = false))
-\/(-\/(FileError.NotFound(nfe.getMessage)))
case e: Exception =>
logger.foreach(_.downloadedArtifact(url, success = false))
throw e
}
finally {
urlLocks.remove(url)
}
@tailrec
def helper(retry: Int): FileError \/ T = {
for (res0 <- res)
logger.foreach(_.downloadedArtifact(url, success = res0.isRight))
val resOpt =
try {
val o = new Object
val prev = urlLocks.putIfAbsent(url, o)
res.merge
} else
-\/(FileError.ConcurrentDownload(url))
}
catch { case e: Exception =>
-\/(FileError.DownloadError(s"Caught $e${Option(e.getMessage).fold("")(" (" + _ + ")")}"))
val res =
if (prev == null) {
logger.foreach(_.downloadingArtifact(url, file))
val res =
try \/-(f)
catch {
case nfe: FileNotFoundException if nfe.getMessage != null =>
logger.foreach(_.downloadedArtifact(url, success = false))
-\/(-\/(FileError.NotFound(nfe.getMessage)))
case e: Exception =>
logger.foreach(_.downloadedArtifact(url, success = false))
throw e
}
finally {
urlLocks.remove(url)
}
for (res0 <- res)
logger.foreach(_.downloadedArtifact(url, success = res0.isRight))
res.merge[FileError \/ T]
} else
-\/(FileError.ConcurrentDownload(url))
Some(res)
}
catch {
case _: javax.net.ssl.SSLException if retry >= 1 =>
// TODO If Cache is made an (instantiated) class at some point, allow to log that exception.
None
case NonFatal(e) =>
Some(-\/(FileError.DownloadError(s"Caught $e${Option(e.getMessage).fold("")(" (" + _ + ")")}")))
}
resOpt match {
case Some(res) => res
case None =>
helper(retry - 1)
}
}
helper(retry)
}
private def temporaryFile(file: File): File = {
val dir = file.getParentFile
val name = file.getName

View File

@ -396,7 +396,14 @@ class Helper(
fetchQuiet
if (verbosityLevel >= 1) {
errPrintln(s" Dependencies:\n${Print.dependenciesUnknownConfigs(dependencies, Map.empty)}")
errPrintln(
s" Dependencies:\n" +
Print.dependenciesUnknownConfigs(
dependencies,
Map.empty,
printExclusions = verbosityLevel >= 2
)
)
if (forceVersions.nonEmpty) {
errPrintln(" Force versions:")
@ -522,9 +529,18 @@ class Helper(
val depsStr =
if (reverseTree || tree)
Print.dependencyTree(dependencies, res, printExclusions = verbosityLevel >= 1, reverse = reverseTree)
Print.dependencyTree(
dependencies,
res,
printExclusions = verbosityLevel >= 1,
reverse = reverseTree
)
else
Print.dependenciesUnknownConfigs(trDeps, projCache)
Print.dependenciesUnknownConfigs(
trDeps,
projCache,
printExclusions = verbosityLevel >= 1
)
if (printResultStdout)
println(depsStr)
@ -554,7 +570,11 @@ class Helper(
anyError = true
errPrintln(
s"\nConflict:\n" +
Print.dependenciesUnknownConfigs(res.conflicts.toVector, projCache)
Print.dependenciesUnknownConfigs(
res.conflicts.toVector,
projCache,
printExclusions = verbosityLevel >= 1
)
)
}

View File

@ -376,7 +376,11 @@ object Resolution {
// although I can find no mention of them in any manual / spec
"pom.groupId" -> project.module.organization,
"pom.artifactId" -> project.module.name,
"pom.version" -> project.version
"pom.version" -> project.version,
// Required by some dependencies too (org.apache.directory.shared:shared-ldap:0.9.19 in particular)
"groupId" -> project.module.organization,
"artifactId" -> project.module.name,
"version" -> project.version
) ++ project.properties ++ Seq(
"project.groupId" -> project.module.organization,
"project.artifactId" -> project.module.name,

View File

@ -29,6 +29,10 @@ object Version {
case (BigNumber(a), Number(b)) => a.compare(b)
case (Qualifier(_, a), Qualifier(_, b)) => a.compare(b)
case (Literal(a), Literal(b)) => a.compareToIgnoreCase(b)
case (BuildMetadata(_), BuildMetadata(_)) =>
// Semver § 10: two versions that differ only in the build metadata, have the same precedence.
// Might introduce some non-determinism though :-/
0
case _ =>
val rel0 = compareToEmpty
@ -67,6 +71,10 @@ object Version {
val order = -1
override def compareToEmpty = if (value.isEmpty) 0 else 1
}
final case class BuildMetadata(value: String) extends Item {
val order = 1
override def compareToEmpty = if (value.isEmpty) 0 else 1
}
case object Min extends Item {
val order = -8
@ -97,6 +105,7 @@ object Version {
case object Dot extends Separator
case object Hyphen extends Separator
case object Underscore extends Separator
case object Plus extends Separator
case object None extends Separator
def apply(s: String): (Item, Stream[(Separator, Item)]) = {
@ -135,6 +144,7 @@ object Version {
case '.' => (Dot, s.tail)
case '-' => (Hyphen, s.tail)
case '_' => (Underscore, s.tail)
case '+' => (Plus, s.tail)
case _ => (None, s)
}
}
@ -143,9 +153,13 @@ object Version {
if (s.isEmpty) Stream()
else {
val (sep, rem0) = parseSeparator(s)
val (item, rem) = parseItem(rem0)
(sep, item) #:: helper(rem)
sep match {
case Plus =>
Stream((sep, BuildMetadata(rem0.mkString)))
case _ =>
val (item, rem) = parseItem(rem0)
(sep, item) #:: helper(rem)
}
}
}
@ -154,51 +168,51 @@ object Version {
}
}
def postProcess(prevIsNumeric: Option[Boolean], item: Item, tokens0: Stream[(Tokenizer.Separator, Item)]): Stream[Item] = {
val tokens = {
var _tokens = tokens0
if (isNumeric(item)) {
val nextNonDotZero = _tokens.dropWhile{case (Tokenizer.Dot, n: Numeric) => n.isEmpty; case _ => false }
if (nextNonDotZero.forall(t => t._1 == Tokenizer.Hyphen || ((t._1 == Tokenizer.Dot || t._1 == Tokenizer.None) && !isNumeric(t._2)))) { // Dot && isNumeric(t._2)
_tokens = nextNonDotZero
}
}
_tokens
}
def ifFollowedByNumberElse(ifFollowedByNumber: Item, default: Item) = {
val followedByNumber = tokens.headOption
.exists{ case (Tokenizer.None, num: Numeric) if !num.isEmpty => true; case _ => false }
if (followedByNumber) ifFollowedByNumber
else default
}
def next =
if (tokens.isEmpty) Stream()
else postProcess(Some(isNumeric(item)), tokens.head._2, tokens.tail)
item match {
case Literal("min") => Min #:: next
case Literal("max") => Max #:: next
case Literal("a") =>
ifFollowedByNumberElse(qualifiersMap("alpha"), item) #:: next
case Literal("b") =>
ifFollowedByNumberElse(qualifiersMap("beta"), item) #:: next
case Literal("m") =>
ifFollowedByNumberElse(qualifiersMap("milestone"), item) #:: next
case _ =>
item #:: next
}
}
def isNumeric(item: Item) = item match { case _: Numeric => true; case _ => false }
def items(repr: String): List[Item] = {
val (first, tokens) = Tokenizer(repr)
def isNumeric(item: Item) = item match { case _: Numeric => true; case _ => false }
def postProcess(prevIsNumeric: Option[Boolean], item: Item, tokens0: Stream[(Tokenizer.Separator, Item)]): Stream[Item] = {
val tokens = {
var _tokens = tokens0
if (isNumeric(item)) {
val nextNonDotZero = _tokens.dropWhile{case (Tokenizer.Dot, n: Numeric) => n.isEmpty; case _ => false }
if (nextNonDotZero.forall(t => t._1 == Tokenizer.Hyphen || ((t._1 == Tokenizer.Dot || t._1 == Tokenizer.None) && !isNumeric(t._2)))) { // Dot && isNumeric(t._2)
_tokens = nextNonDotZero
}
}
_tokens
}
def ifFollowedByNumberElse(ifFollowedByNumber: Item, default: Item) = {
val followedByNumber = tokens.headOption
.exists{ case (Tokenizer.None, num: Numeric) if !num.isEmpty => true; case _ => false }
if (followedByNumber) ifFollowedByNumber
else default
}
def next =
if (tokens.isEmpty) Stream()
else postProcess(Some(isNumeric(item)), tokens.head._2, tokens.tail)
item match {
case Literal("min") => Min #:: next
case Literal("max") => Max #:: next
case Literal("a") =>
ifFollowedByNumberElse(qualifiersMap("alpha"), item) #:: next
case Literal("b") =>
ifFollowedByNumberElse(qualifiersMap("beta"), item) #:: next
case Literal("m") =>
ifFollowedByNumberElse(qualifiersMap("milestone"), item) #:: next
case _ =>
item #:: next
}
}
postProcess(None, first, tokens).toList
}

View File

@ -252,7 +252,7 @@ final case class MavenRepository(
for {
xml <- \/.fromEither(compatibility.xmlParse(str))
_ <- if (xml.label == "project") \/-(()) else -\/("Project definition not found")
proj <- Pom.project(xml)
proj <- Pom.project(xml, relocationAsDependency = true)
} yield proj
def artifactFor(url: String) =

View File

@ -140,7 +140,13 @@ object Pom {
def packagingOpt(pom: Node): Option[String] =
text(pom, "packaging", "").toOption
def project(pom: Node): String \/ Project = {
def project(pom: Node): String \/ Project =
project(pom, relocationAsDependency = false)
def project(
pom: Node,
relocationAsDependency: Boolean
): String \/ Project = {
import Scalaz._
for {
@ -242,10 +248,41 @@ object Pom {
case \/-(d) => d
}
val finalProjModule = projModule.copy(organization = groupId)
val relocationDependencyOpt =
if (relocationAsDependency)
pom.children
.find(_.label == "distributionManagement")
.flatMap(_.children.find(_.label == "relocation"))
.map { n =>
// see https://maven.apache.org/guides/mini/guide-relocation.html
val relocatedGroupId = text(n, "groupId", "").getOrElse(finalProjModule.organization)
val relocatedArtifactId = text(n, "artifactId", "").getOrElse(finalProjModule.name)
val relocatedVersion = text(n, "version", "").getOrElse(version)
"" -> Dependency(
finalProjModule.copy(
organization = relocatedGroupId,
name = relocatedArtifactId
),
relocatedVersion,
"",
Set(),
Attributes("", ""),
optional = false,
transitive = true
)
}
else
None
Project(
projModule.copy(organization = groupId),
finalProjModule,
version,
deps.map {
(relocationDependencyOpt.toList ::: deps).map {
case (config, dep0) =>
val dep = extraAttrsMap.get(dep0.moduleVersion).fold(dep0)(attrs =>
dep0.copy(module = dep0.module.copy(attributes = attrs))
@ -353,10 +390,6 @@ object Pom {
release = text(xmlVersioning, "release", "Release version")
.getOrElse("")
versionsOpt = xmlVersioning.children
.find(_.label == "versions")
.map(_.children.filter(_.label == "version").flatMap(_.children.collectFirst{case Text(t) => t}))
lastUpdatedOpt = text(xmlVersioning, "lastUpdated", "Last update date and time")
.toOption
.flatMap(parseDateTime)
@ -384,7 +417,7 @@ object Pom {
text(_, "localCopy", "Snapshot local copy")
.toOption
)
.collect{
.collect {
case "true" => true
case "false" => false
}

View File

@ -4,16 +4,32 @@ import coursier.core.{ Attributes, Dependency, Module, Orders, Project, Resoluti
object Print {
def dependency(dep: Dependency): String = {
val exclusionsStr = dep.exclusions.toVector.sorted.map {
case (org, name) =>
s"\n exclude($org, $name)"
}.mkString
def dependency(dep: Dependency): String =
dependency(dep, printExclusions = false)
s"${dep.module}:${dep.version}:${dep.configuration}$exclusionsStr"
def dependency(dep: Dependency, printExclusions: Boolean): String = {
def exclusionsStr = dep
.exclusions
.toVector
.sorted
.map {
case (org, name) =>
s"\n exclude($org, $name)"
}
.mkString
s"${dep.module}:${dep.version}:${dep.configuration}" + (if (printExclusions) exclusionsStr else "")
}
def dependenciesUnknownConfigs(deps: Seq[Dependency], projects: Map[(Module, String), Project]): String = {
def dependenciesUnknownConfigs(deps: Seq[Dependency], projects: Map[(Module, String), Project]): String =
dependenciesUnknownConfigs(deps, projects, printExclusions = false)
def dependenciesUnknownConfigs(
deps: Seq[Dependency],
projects: Map[(Module, String), Project],
printExclusions: Boolean
): String = {
val deps0 = deps.map { dep =>
dep.copy(
@ -38,7 +54,7 @@ object Print {
(dep.module.organization, dep.module.name, dep.module.toString, dep.version)
}
deps1.map(dependency).mkString("\n")
deps1.map(dependency(_, printExclusions)).mkString("\n")
}
private def compatibleVersions(first: String, second: String): Boolean = {

View File

@ -4,7 +4,9 @@ final class ResolutionException(
val error: ResolutionError
) extends Exception(
error.message,
error.cause.orNull,
true,
false // don't keep stack trace around (improves readability from the SBT console)
)
error.cause.orNull
) {
// not using the 4-arg Exception constructor, only available with Java >= 7
setStackTrace(Array()) // don't keep stack trace around (improves readability from the SBT console)
}

View File

@ -457,12 +457,24 @@ object Tasks {
val useSbtCredentials = coursierUseSbtCredentials.value
val authenticationByHost =
if (useSbtCredentials) {
val cred = sbt.Keys.credentials.value.map(sbt.Credentials.toDirect)
cred.map { c =>
c.host -> Authentication(c.userName, c.passwd)
}.toMap
} else
if (useSbtCredentials)
sbt.Keys.credentials
.value
.flatMap {
case dc: sbt.DirectCredentials => List(dc)
case fc: sbt.FileCredentials =>
sbt.Credentials.loadCredentials(fc.path) match {
case Left(err) =>
log.warn(s"$err, ignoring it")
Nil
case Right(dc) => List(dc)
}
}
.map { c =>
c.host -> Authentication(c.userName, c.passwd)
}
.toMap
else
Map.empty[String, Authentication]
val authenticationByRepositoryId = coursierCredentials.value.mapValues(_.authentication)

View File

@ -96,14 +96,23 @@ object ToSbt {
def moduleReports(
res: Resolution,
classifiersOpt: Option[Seq[String]],
artifactFileOpt: (Module, String, Artifact) => Option[File]
artifactFileOpt: (Module, String, Artifact) => Option[File],
keepPomArtifact: Boolean = false
) = {
val depArtifacts =
val depArtifacts0 =
classifiersOpt match {
case None => res.dependencyArtifacts
case Some(cl) => res.dependencyClassifiersArtifacts(cl)
}
val depArtifacts =
if (keepPomArtifact)
depArtifacts0
else
depArtifacts0.filter {
case (_, a) => a.attributes != Attributes("pom", "")
}
val groupedDepArtifacts = grouped(depArtifacts)
val versions = res.dependencies.toVector.map { dep =>
@ -155,7 +164,8 @@ object ToSbt {
resolution: Resolution,
configs: Map[String, Set[String]],
classifiersOpt: Option[Seq[String]],
artifactFileOpt: (Module, String, Artifact) => Option[File]
artifactFileOpt: (Module, String, Artifact) => Option[File],
keepPomArtifact: Boolean = false
): sbt.UpdateReport = {
val configReports = configs.map {
@ -163,7 +173,7 @@ object ToSbt {
val configDeps = extends0.flatMap(configDependencies.getOrElse(_, Nil))
val subRes = resolution.subset(configDeps)
val reports = ToSbt.moduleReports(subRes, classifiersOpt, artifactFileOpt)
val reports = ToSbt.moduleReports(subRes, classifiersOpt, artifactFileOpt, keepPomArtifact)
new ConfigurationReport(
config,

View File

@ -0,0 +1,2 @@
scalaVersion := "2.11.8"
credentials += Credentials(file("nope/nope/nope/nope/nope"))

View File

@ -0,0 +1,11 @@
{
val pluginVersion = sys.props.getOrElse(
"plugin.version",
throw new RuntimeException(
"""|The system property 'plugin.version' is not defined.
|Specify this property using the scriptedLaunchOpts -D.""".stripMargin
)
)
addSbtPlugin("io.get-coursier" % "sbt-coursier" % pluginVersion)
}

View File

@ -0,0 +1 @@
object Main extends App

View File

@ -0,0 +1 @@
> update

View File

@ -0,0 +1,28 @@
lazy val noPomCheck = TaskKey[Unit]("noPomCheck")
noPomCheck := {
val configReport = update.value
.configuration("compile")
.getOrElse {
throw new Exception(
"compile configuration not found in update report"
)
}
val artifacts = configReport
.modules
.flatMap(_.artifacts)
.map(_._1)
val pomArtifacts = artifacts
.filter { a =>
a.`type` == "pom" && a.classifier.isEmpty
}
for (a <- pomArtifacts)
streams.value.log.error(s"Found POM artifact $a")
assert(pomArtifacts.isEmpty)
}

View File

@ -0,0 +1,11 @@
{
val pluginVersion = sys.props.getOrElse(
"plugin.version",
throw new RuntimeException(
"""|The system property 'plugin.version' is not defined.
|Specify this property using the scriptedLaunchOpts -D.""".stripMargin
)
)
addSbtPlugin("io.get-coursier" % "sbt-coursier" % pluginVersion)
}

View File

@ -0,0 +1 @@
> noPomCheck

View File

@ -110,7 +110,8 @@ testLauncherJava6() {
}
testSbtCoursierJava6() {
sbt ++${SCALA_VERSION} publishLocal
sbt ++${SCALA_VERSION} coreJVM/publishLocal cache/publishLocal sbt-coursier/publishLocal
git clone https://github.com/alexarchambault/scalacheck-shapeless.git
cd scalacheck-shapeless
cd project
@ -126,6 +127,23 @@ testSbtCoursierJava6() {
openjdk:6-jre \
/bin/bash -c "cd /root/project && /root/bin/sbt update"
cd ..
# ensuring resolution error doesn't throw NoSuchMethodError
mkdir -p foo/project
cd foo
echo 'libraryDependencies += "foo" % "bar" % "1.0"' >> build.sbt
echo 'addSbtPlugin("io.get-coursier" % "sbt-coursier" % "1.0.0-SNAPSHOT")' >> project/plugins.sbt
echo 'sbt.version=0.13.15' >> project/build.properties
docker run -it --rm \
-v $HOME/.ivy2/local:/root/.ivy2/local \
-v $(pwd):/root/project \
-v $(pwd)/../bin:/root/bin \
-e CI=true \
openjdk:6-jre \
/bin/bash -c "cd /root/project && /root/bin/sbt update || true" | tee -a output
grep "coursier.ResolutionException: Encountered 1 error" output
echo "Ok, found ResolutionException in output"
cd ..
}
clean_plugin_sbt() {

View File

@ -0,0 +1,4 @@
bouncycastle:bctsp-jdk14:138:compile
org.bouncycastle:bcmail-jdk14:1.38:compile
org.bouncycastle:bcprov-jdk14:1.38:compile
org.bouncycastle:bctsp-jdk14:1.38:compile

View File

@ -0,0 +1,8 @@
antlr:antlr:2.7.7:compile
commons-collections:commons-collections:3.2.1:compile
commons-lang:commons-lang:2.4:compile
org.apache.directory.shared:shared-asn1:0.9.19:compile
org.apache.directory.shared:shared-i18n:0.9.19:compile
org.apache.directory.shared:shared-ldap:0.9.19:compile
org.apache.directory.shared:shared-ldap-constants:0.9.19:compile
org.slf4j:slf4j-api:1.5.10:compile

View File

@ -579,6 +579,20 @@ object CentralTests extends TestSuite {
assert(urls == expectedZipArtifactUrls)
}
}
'groupIdVersionProperties - {
resolutionCheck(
Module("org.apache.directory.shared", "shared-ldap"),
"0.9.19"
)
}
'relocation - {
resolutionCheck(
Module("bouncycastle", "bctsp-jdk14"),
"138"
)
}
}
}

View File

@ -4,21 +4,25 @@ package test
import utest._
object VersionTests extends TestSuite {
import core.Version
def compare(first: String, second: String) = Version(first).compare(Version(second))
def compare(first: String, second: String) =
Version(first).compare(Version(second))
def increasing(versions: String*): Boolean =
versions.iterator.sliding(2).withPartial(false).forall{case Seq(a, b) => compare(a, b) < 0 }
val tests = TestSuite {
'stackOverflow{
'stackOverflow - {
val s = "." * 100000
val v = Version(s)
assert(v.isEmpty)
}
'empty{
'empty - {
val v0 = Version("0")
val v = Version("")
@ -26,7 +30,7 @@ object VersionTests extends TestSuite {
assert(v.isEmpty)
}
'max{
'max - {
val v21 = Version("2.1")
val v22 = Version("2.2")
val v23 = Version("2.3")
@ -39,21 +43,49 @@ object VersionTests extends TestSuite {
assert(max == v241)
}
'numericOrdering{
assert(compare("1.2", "1.10") < 0)
'buildMetadata - {
* - {
assert(compare("1.2", "1.2+foo") < 0)
// Semver § 10: two versions that differ only in the build metadata, have the same precedence
assert(compare("1.2+bar", "1.2+foo") == 0)
assert(compare("1.2+bar.1", "1.2+bar.2") == 0)
}
'shouldNotParseMetadata - {
* - {
val items = Version("1.2+bar.2").items
val expectedItems = Seq(
Version.Number(1), Version.Number(2), Version.BuildMetadata("bar.2")
)
assert(items == expectedItems)
}
* - {
val items = Version("1.2+bar-2").items
val expectedItems = Seq(
Version.Number(1), Version.Number(2), Version.BuildMetadata("bar-2")
)
assert(items == expectedItems)
}
* - {
val items = Version("1.2+bar+foo").items
val expectedItems = Seq(
Version.Number(1), Version.Number(2), Version.BuildMetadata("bar+foo")
)
assert(items == expectedItems)
}
}
}
// Adapted from aether-core/aether-util/src/test/java/org/eclipse/aether/util/version/GenericVersionTest.java
// Only one test doesn't pass (see FIXME below)
'EmptyVersion{
'emptyVersion - {
assert(compare("0", "" ) == 0)
}
'NumericOrdering
{
'numericOrdering - {
assert(compare("2", "10" ) < 0)
assert(compare("1.2", "1.10" ) < 0)
assert(compare("1.0.2", "1.0.10" ) < 0)
@ -63,16 +95,14 @@ object VersionTests extends TestSuite {
}
'Delimiters
{
'delimiters - {
assert(compare("1.0", "1-0" ) == 0)
assert(compare("1.0", "1_0" ) == 0)
assert(compare("1.a", "1a" ) == 0)
}
'LeadingZerosAreSemanticallyIrrelevant
{
'leadingZerosAreSemanticallyIrrelevant - {
assert(compare("1", "01" ) == 0)
assert(compare("1.2", "1.002" ) == 0)
assert(compare("1.2.3", "1.2.0003" ) == 0)
@ -80,8 +110,7 @@ object VersionTests extends TestSuite {
}
'TrailingZerosAreSemanticallyIrrelevant
{
'trailingZerosAreSemanticallyIrrelevant - {
assert(compare("1", "1.0.0.0.0.0.0.0.0.0.0.0.0.0" ) == 0)
assert(compare("1", "1-0-0-0-0-0-0-0-0-0-0-0-0-0" ) == 0)
assert(compare("1", "1.0-0.0-0.0-0.0-0.0-0.0-0.0" ) == 0)
@ -90,8 +119,7 @@ object VersionTests extends TestSuite {
}
'TrailingZerosBeforeQualifierAreSemanticallyIrrelevant
{
'trailingZerosBeforeQualifierAreSemanticallyIrrelevant - {
assert(compare("1.0-ga", "1.0.0-ga" ) == 0)
assert(compare("1.0.ga", "1.0.0.ga" ) == 0)
assert(compare("1.0ga", "1.0.0ga" ) == 0)
@ -109,8 +137,7 @@ object VersionTests extends TestSuite {
}
'TrailingDelimitersAreSemanticallyIrrelevant
{
'trailingDelimitersAreSemanticallyIrrelevant - {
assert(compare("1", "1............." ) == 0)
assert(compare("1", "1-------------" ) == 0)
assert(compare("1.0", "1............." ) == 0)
@ -118,8 +145,7 @@ object VersionTests extends TestSuite {
}
'InitialDelimiters
{
'initialDelimiters - {
assert(compare("0.1", ".1" ) == 0)
assert(compare("0.0.1", "..1" ) == 0)
assert(compare("0.1", "-1" ) == 0)
@ -127,8 +153,7 @@ object VersionTests extends TestSuite {
}
'ConsecutiveDelimiters
{
'consecutiveDelimiters - {
assert(compare("1.0.1", "1..1" ) == 0)
assert(compare("1.0.0.1", "1...1" ) == 0)
assert(compare("1.0.1", "1--1" ) == 0)
@ -136,20 +161,17 @@ object VersionTests extends TestSuite {
}
'UnlimitedNumberOfVersionComponents
{
'unlimitedNumberOfVersionComponents - {
assert(compare("1.0.1.2.3.4.5.6.7.8.9.0.1.2.10", "1.0.1.2.3.4.5.6.7.8.9.0.1.2.3" ) > 0)
}
'UnlimitedNumberOfDigitsInNumericComponent
{
'unlimitedNumberOfDigitsInNumericComponent - {
assert(compare("1.1234567890123456789012345678901", "1.123456789012345678901234567891" ) > 0)
}
'TransitionFromDigitToLetterAndViceVersaIsEqualivantToDelimiter
{
'transitionFromDigitToLetterAndViceVersaIsEqualivantToDelimiter - {
assert(compare("1alpha10", "1.alpha.10" ) == 0)
assert(compare("1alpha10", "1-alpha-10" ) == 0)
@ -158,8 +180,7 @@ object VersionTests extends TestSuite {
}
'WellKnownQualifierOrdering
{
'wellKnownQualifierOrdering - {
assert(compare("1-alpha1", "1-a1" ) == 0)
assert(compare("1-alpha", "1-beta" ) < 0)
assert(compare("1-beta1", "1-b1" ) == 0)
@ -186,8 +207,7 @@ object VersionTests extends TestSuite {
}
'WellKnownQualifierVersusUnknownQualifierOrdering
{
'wellKnownQualifierVersusUnknownQualifierOrdering - {
assert(compare("1-abc", "1-alpha" ) > 0)
assert(compare("1-abc", "1-beta" ) > 0)
assert(compare("1-abc", "1-milestone" ) > 0)
@ -198,8 +218,7 @@ object VersionTests extends TestSuite {
}
'WellKnownSingleCharQualifiersOnlyRecognizedIfImmediatelyFollowedByNumber
{
'wellKnownSingleCharQualifiersOnlyRecognizedIfImmediatelyFollowedByNumber - {
assert(compare("1.0a", "1.0" ) > 0)
assert(compare("1.0-a", "1.0" ) > 0)
assert(compare("1.0.a", "1.0" ) > 0)
@ -229,16 +248,14 @@ object VersionTests extends TestSuite {
}
'UnknownQualifierOrdering
{
'unknownQualifierOrdering - {
assert(compare("1-abc", "1-abcd" ) < 0)
assert(compare("1-abc", "1-bcd" ) < 0)
assert(compare("1-abc", "1-aac" ) > 0)
}
'CaseInsensitiveOrderingOfQualifiers
{
'caseInsensitiveOrderingOfQualifiers - {
assert(compare("1.alpha", "1.ALPHA" ) == 0)
assert(compare("1.alpha", "1.Alpha" ) == 0)
@ -269,8 +286,7 @@ object VersionTests extends TestSuite {
}
'QualifierVersusNumberOrdering
{
'qualifierVersusNumberOrdering - {
assert(compare("1-ga", "1-1" ) < 0)
assert(compare("1.ga", "1.1" ) < 0)
assert(compare("1-ga", "1.0" ) == 0)
@ -291,8 +307,7 @@ object VersionTests extends TestSuite {
'MinimumSegment
{
'minimumSegment - {
assert(compare("1.min", "1.0-alpha-1" ) < 0)
assert(compare("1.min", "1.0-SNAPSHOT" ) < 0)
assert(compare("1.min", "1.0" ) < 0)
@ -305,8 +320,7 @@ object VersionTests extends TestSuite {
}
'MaximumSegment
{
'maximumSegment - {
assert(compare("1.max", "1.0-alpha-1" ) > 0)
assert(compare("1.max", "1.0-SNAPSHOT" ) > 0)
assert(compare("1.max", "1.0" ) > 0)
@ -319,8 +333,7 @@ object VersionTests extends TestSuite {
}
'VersionEvolution
{
'versionEvolution - {
assert(increasing( "0.9.9-SNAPSHOT", "0.9.9", "0.9.10-SNAPSHOT", "0.9.10", "1.0-alpha-2-SNAPSHOT", "1.0-alpha-2",
"1.0-alpha-10-SNAPSHOT", "1.0-alpha-10", "1.0-beta-1-SNAPSHOT", "1.0-beta-1",
"1.0-rc-1-SNAPSHOT", "1.0-rc-1", "1.0-SNAPSHOT", "1.0", "1.0-sp-1-SNAPSHOT", "1.0-sp-1"))
@ -337,8 +350,7 @@ object VersionTests extends TestSuite {
}
// 'CaseInsensitiveOrderingOfQualifiersIsLocaleIndependent
// {
// 'caseInsensitiveOrderingOfQualifiersIsLocaleIndependent - {
// import java.util.Locale
// val orig = Locale.getDefault
// try {