Ignore builds when comparing. Add more tests. Fix errors in __lt

This commit is contained in:
kikito
2015-10-20 23:43:06 +02:00
parent f558f99022
commit 84e37a1993
2 changed files with 113 additions and 137 deletions

View File

@@ -99,23 +99,13 @@ local function compare(a,b)
return a == b and 0 or a < b and -1 or 1 return a == b and 0 or a < b and -1 or 1
end end
local function compareNilPrereleases(mine, other) local function compareIds(myId, otherId)
if mine == other then return 0 if myId == otherId then return 0
elseif not mine then return 1 elseif not myId then return -1
elseif not other then return -1 elseif not otherId then return 1
end -- else return nil end
end
-- notice that builds compare nils inversely than prereleases (the -1 and 1 are switched) local selfNumber, otherNumber = tonumber(myId), tonumber(otherId)
local function compareNilBuilds(mine, other)
if mine == other then return 0
elseif not mine then return -1
elseif not other then return 1
end -- else return nil
end
local function compareIds(selfId, otherId)
local selfNumber, otherNumber = tonumber(selfId), tonumber(otherId)
if selfNumber and otherNumber then -- numerical comparison if selfNumber and otherNumber then -- numerical comparison
return compare(selfNumber, otherNumber) return compare(selfNumber, otherNumber)
@@ -125,18 +115,16 @@ local function compareIds(selfId, otherId)
elseif otherNumber then elseif otherNumber then
return 1 return 1
else else
return compare(selfId, otherId) -- alphanumerical comparison return compare(myId, otherId) -- alphanumerical comparison
end end
end end
local function smallerIds(mine, other, compareNil) local function smallerIdList(myIds, otherIds)
local myIds, otherIds = splitByDot(mine), splitByDot(other)
local myLength = #myIds local myLength = #myIds
local comparison, myId, otherId local comparison
for i = 1, myLength do for i=1, myLength do
myId, otherId = myIds[i], otherIds[i] comparison = compareIds(myIds[i], otherIds[i])
comparison = compareNil(myId, otherId) or compareIds(myId, otherId)
if comparison ~= 0 then if comparison ~= 0 then
return comparison == -1 return comparison == -1
end end
@@ -147,19 +135,11 @@ local function smallerIds(mine, other, compareNil)
end end
local function smallerPrerelease(mine, other) local function smallerPrerelease(mine, other)
if mine == other or not mine then return false if mine == other or not mine then return false
elseif not other then return true elseif not other then return true
end end
return smallerIds(mine, other, compareNilPrereleases) return smallerIdList(splitByDot(mine), splitByDot(other))
end
local function smallerBuild(mine, other)
if mine == other or not other then return false
elseif not mine then return true
end
return smallerIds(mine, other, compareNilBuilds)
end end
local methods = {} local methods = {}
@@ -179,16 +159,19 @@ function mt:__eq(other)
return self.major == other.major and return self.major == other.major and
self.minor == other.minor and self.minor == other.minor and
self.patch == other.patch and self.patch == other.patch and
self.prerelease == other.prerelease and self.prerelease == other.prerelease
self.build == other.build -- notice that build is ignored for precedence in semver 2.0.0
end end
function mt:__lt(other) function mt:__lt(other)
return self.major < other.major or if self.major ~= other.major then return self.major < other.major end
self.minor < other.minor or if self.minor ~= other.minor then return self.minor < other.minor end
self.patch < other.patch or if self.patch ~= other.patch then return self.patch < other.patch end
smallerPrerelease(self.prerelease, other.prerelease) or return smallerPrerelease(self.prerelease, other.prerelease)
smallerBuild(self.build, other.build) -- notice that build is ignored for precedence in semver 2.0.0
end end
-- This works like the "pessimisstic operator" in Rubygems.
-- if a and b are versions, a ^ b means "b is backwards-compatible with a"
-- in other words, "it's safe to upgrade from a to b"
function mt:__pow(other) function mt:__pow(other)
return self.major == other.major and return self.major == other.major and
self.minor <= other.minor self.minor <= other.minor

View File

@@ -39,59 +39,59 @@ describe('semver', function()
end) end)
describe("from strings", function() describe("from strings", function()
test("1.2.3", function() it("1.2.3", function()
checkVersion( v'1.2.3', 1,2,3) checkVersion( v'1.2.3', 1,2,3)
end) end)
test("10.20.123", function() it("10.20.123", function()
checkVersion( v'10.20.123', 10,20,123) checkVersion( v'10.20.123', 10,20,123)
end) end)
test("2.0", function() it("2.0", function()
checkVersion( v'2.0', 2,0,0) checkVersion( v'2.0', 2,0,0)
end) end)
test("5", function() it("5", function()
checkVersion( v'5', 5,0,0) checkVersion( v'5', 5,0,0)
end) end)
test("1.2.3-alpha", function() it("1.2.3-alpha", function()
checkVersion( v'1.2.3-alpha', 1,2,3,'alpha' ) checkVersion( v'1.2.3-alpha', 1,2,3,'alpha' )
end) end)
test("1.2.3+build.15", function() it("1.2.3+build.15", function()
checkVersion( v'1.2.3+build.15', 1,2,3,nil,'build.15' ) checkVersion( v'1.2.3+build.15', 1,2,3,nil,'build.15' )
end) end)
test("1.2.3-rc1+build.15", function() it("1.2.3-rc1+build.15", function()
checkVersion( v'1.2.3-rc1+build.15', 1,2,3,'rc1','build.15' ) checkVersion( v'1.2.3-rc1+build.15', 1,2,3,'rc1','build.15' )
end) end)
end) end)
describe('errors', function() describe('errors', function()
test('no parameters are passed', function() it('no parameters are passed', function()
assert.error(function() v() end) assert.error(function() v() end)
end) end)
test('negative numbers', function() it('negative numbers', function()
assert.error(function() v(-1, 0, 0) end) assert.error(function() v(-1, 0, 0) end)
assert.error(function() v( 0,-1, 0) end) assert.error(function() v( 0,-1, 0) end)
assert.error(function() v( 0, 0,-1) end) assert.error(function() v( 0, 0,-1) end)
end) end)
test('floats', function() it('floats', function()
assert.error(function() v(.1, 0, 0) end) assert.error(function() v(.1, 0, 0) end)
assert.error(function() v( 0,.1, 0) end) assert.error(function() v( 0,.1, 0) end)
assert.error(function() v( 0, 0,.1) end) assert.error(function() v( 0, 0,.1) end)
end) end)
test('empty string', function() it('empty string', function()
assert.error(function() v("") end) assert.error(function() v("") end)
end) end)
test('garbage at the beginning of the string', function() it('garbage at the beginning of the string', function()
assert.error(function() v("foobar1.2.3") end) assert.error(function() v("foobar1.2.3") end)
end) end)
test('garbage at the end of the string', function() it('garbage at the end of the string', function()
assert.error(function() v("1.2.3foobar") end) assert.error(function() v("1.2.3foobar") end)
end) end)
test('a non-string or number is passed', function() it('a non-string or number is passed', function()
assert.error(function() v({}) end) assert.error(function() v({}) end)
end) end)
test('an invalid prerelease', function() it('an invalid prerelease', function()
assert.error(function() v'1.2.3-%?' end) assert.error(function() v'1.2.3-%?' end)
end) end)
test('an invalid build', function() it('an invalid build', function()
assert.error(function() v'1.2.3+%?' end) assert.error(function() v'1.2.3+%?' end)
end) end)
end) end)
@@ -115,115 +115,112 @@ describe('semver', function()
describe("==", function() describe("==", function()
it("is true when major, minor and patch are the same", function() it("is true when major, minor and patch are the same", function()
assert.equal(v(1,2,3), v'1.2.3') assert.equal(v'1.0.0', v'1.0.0')
end) end)
it("is false when major, minor and patch are not the same", function() it("is false when major, minor, patch or prerelease are not the same", function()
assert.not_equal(v(1,2,3), v(4,5,6)) assert.not_equal(v'1.0.0', v'1.0.1')
assert.not_equal(v'1.0.0', v'1.1.0')
assert.not_equal(v'1.0.0', v'2.0.0')
assert.not_equal(v'1.0.0', v'1.0.0-alpha')
end) end)
it("false if all is the same except the prerelease", function() it("ignores builds", function()
assert.not_equal(v(1,2,3), v'1.2.3-alpha') assert.equal(v'1.2.3', v'1.2.3+1')
end) assert.equal(v'1.2.3+1', v'1.2.3+2')
it("false if all is the same except the build", function()
assert.not_equal(v(1,2,3), v'1.2.3+peter.1')
end) end)
end) end)
describe("<", function() describe("<", function()
test("true if major < minor", function() it("compares correctly when major, minor and patch are equal", function()
assert.less(v'1.100.10', v'2.0.0') assert.not_less(v'1.0.0', v'1.0.0')
assert.not_greater(v'1.0.0', v'1.0.0')
assert.equal(v'1.0.0', v'1.0.0')
end) end)
test("false if major > minor", function()
assert.greater(v'2', v'1') it("#focus prioritizes major over minor", function()
--assert.less(v'1.100.10', v'2.0.0')
assert.not_greater(v'1.100.10', v'2.0.0')
--assert.greater(v'2', v'1')
--assert.not_less(v'2', v'1')
end) end)
test("true if major = major but minor < minor", function() it("when equal major, compares minor", function()
assert.less(v'1.2.0', v'1.3.0') assert.less(v'1.2.0', v'1.3.0')
end) assert.not_greater(v'1.2.0', v'1.3.0')
test("false if minor < minor", function()
assert.greater(v'1.1', v'1.0') assert.greater(v'1.1', v'1.0')
assert.not_less(v'1.1', v'1.0')
end) end)
test("true if major =, minor =, but patch <", function() it("when equal major and minor, compares patch", function()
assert.less(v'0.0.1', v'0.0.10') assert.less(v'0.0.1', v'0.0.10')
end) assert.not_greater(v'0.0.1', v'0.0.10')
test("false if major =, minor =, but patch >", function()
assert.greater(v'0.0.2', v'0.0.1') assert.greater(v'0.0.2', v'0.0.1')
assert.not_less(v'0.0.2', v'0.0.1')
end) end)
describe("prereleases", function() describe("prereleases", function()
test("false if exact same prerelease", function() it("compares correctly when major, minor, patch and prerelease are equal", function()
assert.not_less(v'1.0.0-beta', v'1.0.0-beta') assert.not_less(v'1.0.0-1', v'1.0.0-1')
assert.not_greater(v'1.0.0-1', v'1.0.0-1')
assert.equal(v'1.0.0-1', v'1.0.0-1')
end) end)
test("#focus a prerelease version is less than the official version", function() it("prioritizes non-prereleases over prereleases", function()
assert.less(v'1.0.0-rc1', v'1.0.0') assert.less(v'1.0.0-rc1', v'1.0.0')
assert.not_greater(v'1.0.0-rc1', v'1.0.0') assert.not_greater(v'1.0.0-rc1', v'1.0.0')
assert.greater(v'1.2.3', v'1.2.3-alpha') assert.greater(v'1.2.3', v'1.2.3-alpha')
assert.not_less(v'1.2.3', v'1.2.3-alpha') assert.not_less(v'1.2.3', v'1.2.3-alpha')
end) end)
test("identifiers with only digits are compared numerically", function() it("compares identifiers with only digits numerically", function()
assert.less(v'1.0.0-1', v'1.0.0-2') assert.less(v'1.0.0-1', v'1.0.0-2')
assert.not_greater(v'1.0.0-1', v'1.0.0-2') assert.not_greater(v'1.0.0-1', v'1.0.0-2')
assert.greater(v'1.0.0-2', v'1.0.0-1')
assert.not_less(v'1.0.0-2', v'1.0.0-1')
end) end)
test("idendifiers with letters or dashes are compared lexiconumerically", function() it("compares idendifiers with letters or dashes lexiconumerically", function()
assert.less(v'1.0.0-alpha', v'1.0.0-beta') assert.less(v'1.0.0-alpha', v'1.0.0-beta')
assert.less(v'1.0.0-alpha-10', v'1.0.0-alpha-2')
assert.not_greater(v'1.0.0-alpha', v'1.0.0-beta') assert.not_greater(v'1.0.0-alpha', v'1.0.0-beta')
assert.less(v'1.0.0-alpha-10', v'1.0.0-alpha-2')
assert.not_greater(v'1.0.0-alpha-10', v'1.0.0-alpha-2') assert.not_greater(v'1.0.0-alpha-10', v'1.0.0-alpha-2')
assert.greater(v'1.0.0-beta', v'1.0.0-alpha')
assert.not_less(v'1.0.0-beta', v'1.0.0-alpha')
assert.greater(v'1.0.0-alpha-2', v'1.0.0-alpha-10')
assert.not_less(v'1.0.0-alpha-2', v'1.0.0-alpha-10')
end) end)
test("numerical ids always have less priority than lexiconumericals", function() it("prioritizes lexiconumericals over numbers", function()
assert.less(v'1.0.0-1', v'1.0.0-alpha') assert.less(v'1.0.0-1', v'1.0.0-alpha')
assert.less(v'1.0.0-2', v'1.0.0-1asdf')
assert.not_greater(v'1.0.0-1', v'1.0.0-alpha') assert.not_greater(v'1.0.0-1', v'1.0.0-alpha')
assert.less(v'1.0.0-2', v'1.0.0-1asdf')
assert.not_greater(v'1.0.0-2', v'1.0.0-1asdf') assert.not_greater(v'1.0.0-2', v'1.0.0-1asdf')
assert.greater(v'1.0.0-alpha', v'1.0.0-1')
assert.not_less(v'1.0.0-alpha', v'1.0.0-1')
assert.greater(v'1.0.0-1asdf', v'1.0.0-2')
assert.not_less(v'1.0.0-1asdf', v'1.0.0-2')
end) end)
test("identifiers can be separated by colons; they must be compared individually", function() it("splits identifiers by colons, comparing every pair individually", function()
--assert.less(v'1.0.0-alpha' , v'1.0.0-alpha.1') assert.less(v'1.0.0-alpha', v'1.0.0-alpha.1')
--assert.less(v'1.0.0-alpha.1' , v'1.0.0-beta.2')
--assert.less(v'1.0.0-beta.2' , v'1.0.0-beta.11')
--assert.less(v'1.0.0-beta.11' , v'1.0.0-rc.1')
assert.not_greater(v'1.0.0-alpha', v'1.0.0-alpha.1') assert.not_greater(v'1.0.0-alpha', v'1.0.0-alpha.1')
--assert.not_greater(v'1.0.0-alpha.1', v'1.0.0-beta.2') assert.less(v'1.0.0-alpha.1', v'1.0.0-beta.2')
--assert.not_greater(v'1.0.0-beta.2' , v'1.0.0-beta.11') assert.not_greater(v'1.0.0-alpha.1', v'1.0.0-beta.2')
--assert.not_greater(v'1.0.0-beta.11', v'1.0.0-rc.1') assert.less(v'1.0.0-beta.2', v'1.0.0-beta.11')
assert.not_greater(v'1.0.0-beta.2', v'1.0.0-beta.11')
assert.less(v'1.0.0-beta.11', v'1.0.0-rc.1')
assert.not_greater(v'1.0.0-beta.11', v'1.0.0-rc.1')
assert.greater(v'1.0.0-alpha.1', v'1.0.0-alpha')
assert.not_less(v'1.0.0-alpha.1', v'1.0.0-alpha')
assert.greater(v'1.0.0-beta.2', v'1.0.0-alpha.1')
assert.not_less(v'1.0.0-beta.2', v'1.0.0-alpha.1')
assert.greater(v'1.0.0-beta.11', v'1.0.0-beta.2')
assert.not_less(v'1.0.0-beta.11', v'1.0.0-beta.2')
assert.greater(v'1.0.0-rc.1', v'1.0.0-beta.11')
assert.not_less(v'1.0.0-rc.1', v'1.0.0-beta.11')
end) end)
end) end)
describe("builds", function() describe("builds", function()
test("false if exact same build", function() it("false independently of the build", function()
assert.not_less(v'1.0.0+build1', v'1.0.0+build1') assert.not_less(v'1.0.0+build1', v'1.0.0')
assert.not_less(v'1.0.0+build1', v'1.0.0+build3')
assert.not_less(v'1.0.0-beta+build1', v'1.0.0-beta+build2')
assert.not_greater(v'1.0.0+build1', v'1.0.0')
assert.not_greater(v'1.0.0+build1', v'1.0.0+build3')
assert.not_greater(v'1.0.0-beta+build1', v'1.0.0-beta+build2')
end) end)
test("a regular (not-build) version is always less than a build version", function()
assert.less(v'1.0.0', v'1.0.0+12')
assert.less(v'1.0.0', v'1.0.0+12')
end)
test("identifiers with only digits are compared numerically", function()
assert.less(v'1.0.0+1', v'1.0.0+2')
assert.less(v'1.0.0+2', v'1.0.0+10')
assert.not_greater(v'1.0.0+1', v'1.0.0+2')
assert.not_greater(v'1.0.0+2', v'1.0.0+10')
end)
test("idendifiers with letters or dashes are compared lexiconumerically", function()
assert.less(v'1.0.0+build1' , v'1.0.0+build2')
assert.less(v'1.0.0+build10', v'1.0.0+build2')
assert.not_greater(v'1.0.0+build1' , v'1.0.0+build2')
assert.not_greater(v'1.0.0+build10' , v'1.0.0+build2')
end)
test("numerical ids always have less priority than lexiconumericals", function()
assert.less(v'1.0.0+1', v'1.0.0+build1')
assert.less(v'1.0.0+2', v'1.0.0+1build')
assert.not_greater(v'1.0.0+1', v'1.0.0+build1')
assert.not_greater(v'1.0.0+2', v'1.0.0+1build')
end)
test("identifiers can be separated by colons; they must be compared individually", function()
assert.less(v'1.0.0+0.3.7', v'1.3.7+build')
assert.less(v'1.3.7+build', v'1.3.7+build.2.b8f12d7')
assert.less(v'1.3.7+build.2.b8f12d7', v'1.3.7+build.11.e0f985a')
assert.not_greater(v'1.0.0+0.3.7', v'1.3.7+build')
assert.not_greater(v'1.3.7+build', v'1.3.7+build.2.b8f12d7')
assert.not_greater(v'1.3.7+build.2.b8f12d7', v'1.3.7+build.11.e0f985a')
end)
end)
test("#focus prereleases + builds", function()
--assert.less(v'1.0.0-rc.1', v'1.0.0-rc.1+build.1')
--assert.less(v'1.0.0-rc.1+build.1', v'1.0.0')
--assert.not_greater(v'1.0.0-rc.1', v'1.0.0-rc.1+build.1')
assert.not_greater(v'1.0.0-rc.1+build.1', v'1.0.0')
end) end)
end) end)
@@ -254,30 +251,26 @@ describe('semver', function()
end) end)
end) end)
-- This works like the "pessimisstic operator" in Rubygems.
-- if a and b are versions, a ^ b means "b is backwards-compatible with a"
-- in other words, "it's safe to upgrade from a to b"
describe("^", function() describe("^", function()
test("true for self", function() it("true for self", function()
assert.is_true(v(1,2,3) ^ v(1,2,3)) assert.is_true(v(1,2,3) ^ v(1,2,3))
end) end)
test("different major versions mean it's always unsafe", function() it("different major versions mean it's always unsafe", function()
assert.is_false(v(2,0,0) ^ v(3,0,0)) assert.is_false(v(2,0,0) ^ v(3,0,0))
assert.is_false(v(2,0,0) ^ v(1,0,0)) assert.is_false(v(2,0,0) ^ v(1,0,0))
end) end)
test("patches, prereleases and builds are ignored", function() it("patches, prereleases and builds are ignored", function()
assert.is_true(v(1,2,3) ^ v(1,2,0)) assert.is_true(v(1,2,3) ^ v(1,2,0))
assert.is_true(v(1,2,3) ^ v(1,2,5)) assert.is_true(v(1,2,3) ^ v(1,2,5))
assert.is_true(v(1,2,3,'foo') ^ v(1,2,3)) assert.is_true(v(1,2,3,'foo') ^ v(1,2,3))
assert.is_true(v(1,2,3,nil,'bar') ^ v(1,2,3)) assert.is_true(v(1,2,3,nil,'bar') ^ v(1,2,3))
end) end)
test("it's safe to upgrade to a newer minor version", function() it("is safe to upgrade to a newer minor version", function()
assert.is_true(v(1,2,0) ^ v(1,5,0)) assert.is_true(v(1,2,0) ^ v(1,5,0))
end) end)
test("it's unsafe to downgrade to an earlier minor version", function() it("is unsafe to downgrade to an earlier minor version", function()
assert.is_false(v(1,5,0) ^ v(1,2,0)) assert.is_false(v(1,5,0) ^ v(1,2,0))
end) end)
end) end)