codec-rpm-0.1.0: tests/Codec/RPM/VersionSpec.hs
{-# LANGUAGE OverloadedStrings #-}
module Codec.RPM.VersionSpec (spec) where
import Test.Hspec
import Data.Foldable(forM_)
import qualified Data.Text as T
import Codec.RPM.Version(DepRequirement(..), EVR(..), parseDepRequirement, parseEVR, satisfies, vercmp)
import qualified Codec.RPM.Version as RPM(DepOrdering(..))
spec :: Spec
spec = do
describe "Codec.RPM.Version.vercmp" $ do
let vercmpCases = [
("1.0", "1.0", EQ),
("1.0", "2.0", LT),
("2.0", "1.0", GT),
("2.0.1", "2.0.1", EQ),
("2.0", "2.0.1", LT),
("2.0.1", "2.0", GT),
("2.0.1a", "2.0.1a", EQ),
("2.0.1a", "2.0.1", GT),
("2.0.1", "2.0.1a", LT),
("5.5p1", "5.5p1", EQ),
("5.5p1", "5.5p2", LT),
("5.5p2", "5.5p1", GT),
("5.5p10", "5.5p10", EQ),
("5.5p1", "5.5p10", LT),
("5.5p10", "5.5p1", GT),
("10xyz", "10.1xyz", LT),
("10.1xyz", "10xyz", GT),
("xyz10", "xyz10", EQ),
("xyz10", "xyz10.1", LT),
("xyz10.1", "xyz10", GT),
("xyz.4", "xyz.4", EQ),
("xyz.4", "8", LT),
("8", "xyz.4", GT),
("xyz.4", "2", LT),
("2", "xyz.4", GT),
("5.5p2", "5.6p1", LT),
("5.6p1", "5.5p2", GT),
("5.6p1", "6.5p1", LT),
("6.5p1", "5.6p1", GT),
("6.0.rc1", "6.0", GT),
("6.0", "6.0.rc1", LT),
("10b2", "10a1", GT),
("10a2", "10b2", LT),
("1.0aa", "1.0aa", EQ),
("1.0a", "1.0aa", LT),
("1.0aa", "1.0a", GT),
("10.0001", "10.0001", EQ),
("10.0001", "10.1", EQ),
("10.1", "10.0001", EQ),
("10.0001", "10.0039", LT),
("10.0039", "10.0001", GT),
("4.999.9", "5.0", LT),
("5.0", "4.999.9", GT),
("20101121", "20101121", EQ),
("20101121", "20101122", LT),
("20101122", "20101121", GT),
("2_0", "2_0", EQ),
("2.0", "2_0", EQ),
("2_0", "2.0", EQ),
("a", "a", EQ),
("a+", "a+", EQ),
("a+", "a_", EQ),
("a_", "a+", EQ),
("+a", "+a", EQ),
("+a", "_a", EQ),
("_a", "+a", EQ),
("+_", "+_", EQ),
("_+", "+_", EQ),
("_+", "_+", EQ),
("+", "_", EQ),
("_", "+", EQ),
("1.0~rc1", "1.0~rc1", EQ),
("1.0~rc1", "1.0", LT),
("1.0", "1.0~rc1", GT),
("1.0~rc1", "1.0~rc2", LT),
("1.0~rc2", "1.0~rc1", GT),
("1.0~rc1~git123", "1.0~rc1~git123", EQ),
("1.0~rc1~git123", "1.0~rc1", LT),
("1.0~rc1", "1.0~rc1~git123", GT)
]
forM_ vercmpCases $ \(verA, verB, ord) ->
it (T.unpack verA ++ " " ++ show ord ++ " " ++ T.unpack verB) $
vercmp verA verB `shouldBe` ord
describe "Codec.RPM.Version.EVR Ord" $ do
let ordCases = [
(EVR{epoch=Nothing, version="1.0", release="1"}, EVR{epoch=Nothing, version="1.0", release="1"}, EQ),
(EVR{epoch=Just 0, version="1.0", release="1"}, EVR{epoch=Nothing, version="1.0", release="1"}, EQ),
(EVR{epoch=Just 1, version="1.0", release="1"}, EVR{epoch=Nothing, version="1.0", release="1"}, GT),
(EVR{epoch=Nothing, version="1.0", release="1"}, EVR{epoch=Nothing, version="1.1", release="1"}, LT),
(EVR{epoch=Nothing, version="1.0", release="1"}, EVR{epoch=Nothing, version="1.0", release="2"}, LT),
(EVR{epoch=Just 8, version="3.6.9", release="11.fc100"}, EVR{epoch=Just 3, version="3.6.9", release="11.fc100"}, GT),
(EVR{epoch=Just 8, version="3.6.9", release="11.fc100"}, EVR{epoch=Just 11, version="3.6.9", release="11.fc100"}, LT),
(EVR{epoch=Just 8, version="3.6.9", release="11.fc100"}, EVR{epoch=Just 8, version="7.0", release="11.fc100"}, LT),
(EVR{epoch=Just 8, version="3.6.9", release="11.fc100"}, EVR{epoch=Just 8, version="", release="11.fc100"}, GT),
(EVR{epoch=Just 8, version="", release="11.fc100"}, EVR{epoch=Just 8, version="", release="11.fc100"}, EQ),
(EVR{epoch=Nothing, version="1.1", release="1"}, EVR{epoch=Nothing, version="1.01", release="1"}, EQ),
(EVR{epoch=Nothing, version="1..1", release="1"}, EVR{epoch=Nothing, version="1.1", release="1"}, EQ)
]
forM_ ordCases $ \(verA, verB, ord) ->
it (show verA ++ " " ++ show ord ++ " " ++ show verB) $
compare verA verB `shouldBe` ord
describe "Codec.RPM.Version.satisfies" $ do
let satisfiesCases = [
("no", "match", False),
("thing", "thing", True),
("thing", "thing >= 1.0-1", True),
("thing >= 1.0-1", "thing", True),
("thing = 1.0-1", "thing = 1.0-1", True),
("thing = 1.0-1", "thing >= 1.0-1", True),
("thing = 1.0-1", "thing > 1.0-1", False),
("thing = 1.0-1", "thing < 1.0-1", False),
("thing = 1.0-1", "thing <= 1.0-1", True),
("thing = 1.0", "thing = 1.0-9", True),
("thing = 1.0", "thing < 1.0-9", True),
("thing = 1.0", "thing <= 1.0-9", True),
("thing = 1.0", "thing >= 1.0-9", True),
("thing = 1.0", "thing > 1.0-9", True),
("thing = 1.0", "thing = 1.0", True),
("thing = 1.0", "thing < 1.0", False),
("thing = 1.0", "thing > 1.0", False),
("thing = 1.0", "thing >= 1.0", True),
("thing = 1.0", "thing <= 1.0", True),
("thing < 1.0", "thing = 1.0", False),
("thing < 1.0", "thing < 1.0", True),
("thing < 1.0", "thing > 1.0", False),
("thing < 1.0", "thing >= 1.0", False),
("thing < 1.0", "thing <= 1.0", True),
("thing > 1.0", "thing = 1.0", False),
("thing > 1.0", "thing < 1.0", False),
("thing > 1.0", "thing > 1.0", True),
("thing > 1.0", "thing >= 1.0", True),
("thing > 1.0", "thing <= 1.0", False),
("thing >= 1.0", "thing = 1.0", True),
("thing >= 1.0", "thing < 1.0", False),
("thing >= 1.0", "thing > 1.0", True),
("thing >= 1.0", "thing >= 1.0", True),
("thing >= 1.0", "thing <= 1.0", True),
("thing <= 1.0", "thing = 1.0", True),
("thing <= 1.0", "thing < 1.0", True),
("thing <= 1.0", "thing > 1.0", False),
("thing <= 1.0", "thing >= 1.0", True),
("thing <= 1.0", "thing <= 1.0", True),
-- test versions without releases
("thing <= 1.0", "thing = 1.0-9", True),
("thing <= 1.0", "thing < 1.0-9", True),
("thing <= 1.0", "thing <= 1.0-9", True),
("thing <= 1.0", "thing >= 1.0-9", True),
("thing <= 1.0", "thing > 1.0-9", True),
("thing >= 1.0", "thing = 1.0-9", True),
("thing >= 1.0", "thing < 1.0-9", True),
("thing >= 1.0", "thing <= 1.0-9", True),
("thing >= 1.0", "thing >= 1.0-9", True),
("thing >= 1.0", "thing > 1.0-9", True),
-- test versions without releases by swapping the order
("thing <= 1.0-8", "thing = 1.0", True),
("thing <= 1.0-8", "thing < 1.0", True),
("thing <= 1.0-8", "thing <= 1.0", True),
("thing <= 1.0-8", "thing >= 1.0", True),
("thing <= 1.0-8", "thing > 1.0", True),
("thing >= 1.0-8", "thing = 1.0", True),
("thing >= 1.0-8", "thing < 1.0", False),
("thing >= 1.0-8", "thing <= 1.0", True),
("thing >= 1.0-8", "thing >= 1.0", True),
("thing >= 1.0-8", "thing > 1.0", True),
("thing = 1.0-9", "thing = 1.0-9", True),
("thing < 1.0-9", "thing = 1.0-9", False),
("thing <= 1.0-9", "thing = 1.0-9", True),
("thing > 1.0-9", "thing = 1.0-9", False),
("thing >= 1.0-9", "thing = 1.0-9", True),
("thing >= 1.0-1", "thing = 1.0-1", True),
("thing >= 1.0-1", "thing >= 1.0-1", True),
("thing >= 1.0-1", "thing > 1.0-1", True),
("thing >= 1.0-1", "thing < 1.0-1", False),
("thing >= 1.0-1", "thing <= 1.0-1", True),
("thing > 1.0-1", "thing = 1.0-1", False),
("thing > 1.0-1", "thing >= 1.0-1", True),
("thing > 1.0-1", "thing > 1.0-1", True),
("thing > 1.0-1", "thing < 1.0-1", False),
("thing > 1.0-1", "thing <= 1.0-1", False),
("thing < 1.0-1", "thing = 1.0-1", False),
("thing < 1.0-1", "thing >= 1.0-1", False),
("thing < 1.0-1", "thing > 1.0-1", False),
("thing < 1.0-1", "thing < 1.0-1", True),
("thing < 1.0-1", "thing <= 1.0-1", True),
("thing <= 1.0-1", "thing = 1.0-1", True),
("thing <= 1.0-1", "thing >= 1.0-1", True),
("thing <= 1.0-1", "thing > 1.0-1", False),
("thing <= 1.0-1", "thing < 1.0-1", True),
("thing <= 1.0-1", "thing <= 1.0-1", True),
("thing = 9.0", "thing = 1.0-1", False),
("thing = 9.0", "thing >= 1.0-1", True),
("thing = 9.0", "thing > 1.0-1", True),
("thing = 9.0", "thing <= 1.0-1", False),
("thing = 9.0", "thing < 1.0-1", False),
("thing < 9.0", "thing = 1.0-1", True),
("thing < 9.0", "thing >= 1.0-1", True),
("thing < 9.0", "thing > 1.0-1", True),
("thing < 9.0", "thing <= 1.0-1", True),
("thing < 9.0", "thing < 1.0-1", True),
("thing <= 9.0", "thing = 1.0-1", True),
("thing <= 9.0", "thing >= 1.0-1", True),
("thing <= 9.0", "thing > 1.0-1", True),
("thing <= 9.0", "thing <= 1.0-1", True),
("thing <= 9.0", "thing < 1.0-1", True),
("thing > 9.0", "thing = 1.0-1", False),
("thing > 9.0", "thing >= 1.0-1", True),
("thing > 9.0", "thing > 1.0-1", True),
("thing > 9.0", "thing <= 1.0-1", False),
("thing > 9.0", "thing < 1.0-1", False),
("thing >= 9.0", "thing = 1.0-1", False),
("thing >= 9.0", "thing >= 1.0-1", True),
("thing >= 9.0", "thing > 1.0-1", True),
("thing >= 9.0", "thing <= 1.0-1", False),
("thing >= 9.0", "thing < 1.0-1", False),
("thing = 1.0", "thing = 9.0-1", False),
("thing = 1.0", "thing >= 9.0-1", False),
("thing = 1.0", "thing > 9.0-1", False),
("thing = 1.0", "thing <= 9.0-1", True),
("thing = 1.0", "thing < 9.0-1", True),
("thing < 1.0", "thing = 9.0-1", False),
("thing < 1.0", "thing >= 9.0-1", False),
("thing < 1.0", "thing > 9.0-1", False),
("thing < 1.0", "thing <= 9.0-1", True),
("thing < 1.0", "thing < 9.0-1", True),
("thing <= 1.0", "thing = 9.0-1", False),
("thing <= 1.0", "thing >= 9.0-1", False),
("thing <= 1.0", "thing > 9.0-1", False),
("thing <= 1.0", "thing <= 9.0-1", True),
("thing <= 1.0", "thing < 9.0-1", True),
("thing >= 1.0", "thing = 9.0-1", True),
("thing >= 1.0", "thing >= 9.0-1", True),
("thing >= 1.0", "thing > 9.0-1", True),
("thing >= 1.0", "thing <= 9.0-1", True),
("thing >= 1.0", "thing < 9.0-1", True),
("thing > 1.0", "thing = 9.0-1", True),
("thing > 1.0", "thing >= 9.0-1", True),
("thing > 1.0", "thing > 9.0-1", True),
("thing > 1.0", "thing <= 9.0-1", True),
("thing > 1.0", "thing < 9.0-1", True)
]
forM_ satisfiesCases $ \(v1, v2, b) ->
it (T.unpack v1 ++ " " ++ T.unpack v2 ++ ": " ++ show b) $
case (parseDepRequirement v1, parseDepRequirement v2) of
(Right verA, Right verB) -> satisfies verA verB `shouldBe` b
_ -> expectationFailure "Unable to parse versions"
describe "Codec.RPM.Version.parseEVR" $ do
let parseEVRCases = [
("1.0-11.fc100", Right EVR{epoch=Nothing, version="1.0", release="11.fc100"}),
("0:1.0-11.fc100", Right EVR{epoch=Just 0, version="1.0", release="11.fc100"}),
("8:1.0-11.fc100", Right EVR{epoch=Just 8, version="1.0", release="11.fc100"}),
("1.0", Right EVR{epoch=Nothing, version="1.0", release=""}),
("8:1.0", Right EVR{epoch=Just 8, version="1.0", release=""}),
-- missing epoch
(":1.0-11.fc100", Left ()),
-- missing version
("0:-11.fc100", Left ()),
-- missing release
("0:1.0-", Left ()),
-- invalid epochs
("-1:1.0-100.fc11", Left ()),
("A:1.0-100.fc11", Left ()),
("8589934592:1.0-100.fc11", Left ()),
-- invalid versions
("0:1.0:0-100.fc11", Left ()),
("0:1.0&0-100.fc11", Left ()),
("0:1.0\x01f32e\&0-100.fc11", Left ()),
-- invalid releases
("0:1.0-100.fc:11", Left ()),
("0:1.0-100.fc&11", Left ()),
("0:1.0-100.fc\x01f32e\&11", Left ())
]
-- For the error cases, we don't actually care about the contents of the error, so just
-- make sure parseEVR returns a Left. Also, the Either returned by parseEVR is not the
-- same type as the ones in the test cases (ParseError vs. ()), so unwrap the Right EVR
-- cases to compare.
-- Making fake ParseErrors is hard, so the Either returned by parseEVR is not the same type
-- as the either in the test data (ParseError vs. ()). Unwrap the Right values to compare EVRs,
-- and for parse errors just check that parseEVR returns a Left.
forM_ parseEVRCases $ \(str, result) ->
it (T.unpack str) $ case (result, parseEVR str) of
(Right evr1, Right evr2) -> evr1 `shouldBe` evr2
(Left _, Right evr) -> expectationFailure $ "bad string parsed as: " ++ show evr
(Right _, Left err) -> expectationFailure $ "unable to parse valid EVR: " ++ show err
_ -> return ()
describe "Codec.RPM.Version.parseDepRequirement" $ do
let parseDepRequirementCases = [
("libthing", DepRequirement "libthing" Nothing),
("libthing >= 1.0", DepRequirement "libthing" $ Just (RPM.GTE, EVR{epoch=Nothing, version="1.0", release=""})),
("libthing > 1.0", DepRequirement "libthing" $ Just (RPM.GT, EVR{epoch=Nothing, version="1.0", release=""})),
("libthing = 1.0", DepRequirement "libthing" $ Just (RPM.EQ, EVR{epoch=Nothing, version="1.0", release=""})),
("libthing < 1.0", DepRequirement "libthing" $ Just (RPM.LT, EVR{epoch=Nothing, version="1.0", release=""})),
("libthing <= 1.0", DepRequirement "libthing" $ Just (RPM.LTE, EVR{epoch=Nothing, version="1.0", release=""})),
-- sometimes an RPM will have an invalid version in a Requires/Provides line.
-- make sure the whole thing gets parsed as a name
("httpd-mmn = 20120211-x86-64", DepRequirement "httpd-mmn = 20120211-x86-64" Nothing)
]
forM_ parseDepRequirementCases $ \(str, result) ->
it (T.unpack str) $ case parseDepRequirement str of
Right dr -> dr `shouldBe` result
Left err -> expectationFailure $ "failed to parse DepRequirement: " ++ show err