ExactVersionMatcher, which is used by the VersionRangeMatcher (e.g. looking for a version like “[0.5.9, 0.5.9.20140812-211212]”) uses a collation order for comparing that does not match the standard collation sequences as defined by ASCII (http://tools.ietf.org/html/rfc20) or UTF (http://www.unicode.org/charts/PDF/U0000.pdf and http://www.unicode.org/charts/PDF/UFF00.pdf)
As opposed to any of the widely used Latin character sets, ExactVersionMatcher treats Digits to be greater than Characters.
import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.ExactVersionMatcher
import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionRangeMatcher
import spock.lang.Specification
public class VersionRangeMatcherTest extends Specification {
def matcher = new VersionRangeMatcher(new ExactVersionMatcher())
def test() {
expect:
matcher.accept(selector, candidate) == result
where:
selector
| candidate
| result
// This is how it works now:
"[0.5.9.0, 0.5.9.local]"
| "0.5.9.local"
| false
// local is not between "0" and "local"
"[0.5.9.a, 0.5.9.local]"
| "0.5.9.local"
| true
// but it is between "a" and "local"
"[0.5.9.a, 0.5.9.20140812-121314]" | "0.5.9.local"
| true
// This is how I expect it to work from ASCII/UTF collation sequence:
"[0.5.9.0, 0.5.9.local]"
| "0.5.9.local"
| true
// local IS between "0" and "local" (incluseive)
"[0.5.9.a, 0.5.9.local]"
| "0.5.9.local"
| true
// AND it is between "a" and "local"
"[0.5.9.a, 0.5.9.20140812-121314]" | "0.5.9.local"
| false
// IS NOT between "a" and "2014..:" ("local" > "2014..."
}
}
A very simple fix would be in ExactVersionMatcher, around lines 79-84 to switch the return values of:
if (is1Number && !is2Number) {
return 1;
}
if (is2Number && !is1Number) {
return -1;
}
However, this might of course have big implications on code that relies on the current (wrong) interpretation. I also had a look at the original Ivy org.apache.ivy.plugins.version.VersionRangeMatcher and especially at the LatestRevisionStrategy that is used for comparing concrete version like ExactVersionMatcher does in gradle and saw that the compare logic looks quite similar and in fact my unit tests fail in the same way.
I guess this “works as designed”?