cabal-gild-0.2.0.1: source/test-suite/CabalGild/Test/Interval.hs
{-# OPTIONS_GHC -Wno-orphans #-}
module CabalGild.Test.Interval where
import Control.Monad (replicateM)
import Distribution.Parsec (eitherParsec)
import Distribution.Types.Version
( Version,
mkVersion,
validVersion,
versionNumbers,
)
import Distribution.Types.VersionRange
( VersionRange,
VersionRangeF (..),
anyVersion,
earlierVersion,
intersectVersionRanges,
laterVersion,
majorBoundVersion,
notThisVersion,
orEarlierVersion,
orLaterVersion,
projectVersionRange,
thisVersion,
unionVersionRanges,
withinRange,
withinVersion,
)
import Distribution.Version (transformCaretUpper)
import Math.NumberTheory.Logarithms (intLog2)
import Test.QuickCheck (Arbitrary (..), (===))
import qualified Test.QuickCheck as QC
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit (testCase, (@?=))
import Test.Tasty.QuickCheck (testProperty)
import VersionInterval
tests :: TestTree
tests =
testGroup
"version-interval"
[ testGroup
"Validity"
[ testProperty "validVersion" validVersion,
testProperty "validVersionInterval" validVersionInterval,
testProperty "validVersionIntervals" $ \vr ->
let intervals = toVersionIntervals vr
in QC.counterexample (show intervals) $ validVersionIntervals intervals
],
testGroup
"VersionInterval"
[ testProperty "intersect valid" $ \a b ->
let ab = intersectInterval a b
in maybe (QC.property True) (\ab' -> QC.counterexample ("intersection: " ++ show ab') $ validVersionInterval ab') ab,
testProperty "intersect complete" intersectComplete,
testProperty "intersect complete lax" intersectCompleteLax
],
testGroup
"stage1"
[ testProperty "valid" stage1_valid,
testProperty "complete" stage1_complete,
testProperty "complete lax" stage1_complete_lax,
testProperty "valid ex1" $
stage1_valid
(intersectVersionRanges (majorBoundVersion (mkVersion [0])) (thisVersion (mkVersion [1]))),
testProperty "valid ex2" $
stage1_valid
(intersectVersionRanges (majorBoundVersion (mkVersion [0])) (orLaterVersion (mkVersion [1]))),
testProperty "complete ex1" $
stage1_complete
(intersectVersionRanges (majorBoundVersion (mkVersion [0])) (thisVersion (mkVersion [6])))
(mkVersion [6]),
testProperty "complete ex2" $
stage1_complete
(intersectVersionRanges (majorBoundVersion (mkVersion [0])) (orLaterVersion (mkVersion [1]))),
testProperty "complete lax ex2" $
stage1_complete_lax
(intersectVersionRanges (majorBoundVersion (mkVersion [0])) (orLaterVersion (mkVersion [1])))
],
testGroup
"stage2"
[ testProperty "valid" stage2_valid,
testProperty "involutive" stage2_involutive,
testProperty "complete" stage2_complete,
testProperty "complete_lax" stage2_complete_lax
],
testGroup
"stage3"
[ testProperty "valid" stage3_valid,
testProperty "involutive" stage3_involutive,
testProperty "complete" stage3_complete,
testProperty "complete_lax" stage3_complete_lax,
testProperty "complete ex1" $
stage3_complete
[VI (LB (mkVersion [0])) (MB (mkVersion [0])) NoUB, VI (LB (mkVersion [0, 0])) NoMB NoUB]
(mkVersion [0]),
testProperty "complete lax ex1" $
stage3_complete_lax
[VI (LB (mkVersion [0, 0])) (MB (mkVersion [0])) (UB (mkVersion [1])), VI (LB (mkVersion [0, 0, 0])) (MB (mkVersion [1])) (UB (mkVersion [1]))]
(mkVersion [1]),
testProperty "valid ex2" $
stage3_valid
(stage1 id (intersectVersionRanges (majorBoundVersion (mkVersion [0])) (orLaterVersion (mkVersion [1])))),
testProperty "complete ex2" $
stage3_complete
(stage1 id (intersectVersionRanges (majorBoundVersion (mkVersion [0])) (orLaterVersion (mkVersion [1])))),
testProperty "complete lax ex2" $
stage3_complete_lax
(stage1 id (intersectVersionRanges (majorBoundVersion (mkVersion [0])) (orLaterVersion (mkVersion [1])))),
testProperty "valid ex3" $
stage3_valid
[VI (LB (mkVersion [0, 0])) (MB (mkVersion [0])) NoUB, VI (LB (mkVersion [0, 0])) NoMB NoUB]
],
testGroup
"normalise"
[ normaliseExample ">=1 && <2" ">=1 && <2",
normaliseExample "^>=1" "^>=1",
normaliseExample "^>=1 || ^>=2" "^>=1 || ^>=2",
normaliseExample "^>=1.2 || ^>=1.3 || ^>=1.4" "^>=1.2 || ^>=1.3 || ^>=1.4",
normaliseExample "^>=1.2 || ^>=2.0" "^>=1.2 || ^>=2.0",
normaliseExample ">=1.2 && <1.4 || ^>=1.4 || ^>=1.5" "^>=1.2 || ^>=1.3 || ^>=1.4 || ^>=1.5",
normaliseExample ">=1.2 && <2.4 || ^>=2.4 || ^>=2.5" ">=1.2 && <2.5 || ^>=2.5",
normaliseExample "^>=1.2.0.0 || ^>=1.3.0.0 || ^>=1.4.0.0" "^>=1.2.0.0 || ^>=1.3.0.0 || ^>=1.4.0.0",
normaliseExample "==3.1.4" "==3.1.4",
normaliseExample "<3.1.4 || >3.1.4" "<3.1.4 || >=3.1.4.0",
normaliseExample "^>=1.2 && <2.0 || ^>=2.0" "^>=1.2 || ^>=2.0",
normaliseExample "^>=1.2 && <3.0 || ^>=2.0" "^>=1.2 || ^>=2.0",
normaliseExample "^>=1.2 || ^>=2.0 && <3" "^>=1.2 || ^>=2.0",
normaliseExample "(^>=1.2 || ^>=2.0) && <3" "^>=1.2 && <2.0 || ^>=2.0 && <3",
normaliseExample "(>=1.2 || >=2.0) && <3" ">=1.2 && <3",
normaliseExample "<1 || >=1.0" "<1 || >=1.0",
normaliseExample "<=1 || >=1.0" ">=0",
normaliseExample ">0 && <0.0" "<0",
normaliseExample
"^>=1.5.0.1 || ^>=1.6.0.1 || >=1.9 && <1.13"
"^>=1.5.0.1 || ^>=1.6.0.1 || ^>=1.9 || ^>=1.10 || ^>=1.11 ||^>=1.12",
cannotNormaliseExample "^>=0 && >=0.1" IntervalsEmpty,
testProperty "involutive" normaliseInvolutive,
testProperty "complete" normaliseComplete,
testProperty "complete_lax" normaliseCompleteLax,
testProperty "involutive ex1" $
normaliseInvolutive $
intersectVersionRanges (majorBoundVersion (mkVersion [3])) (laterVersion (mkVersion [3]))
]
]
normaliseExample :: String -> String -> TestTree
normaliseExample input expected = testCase input $ do
input' <- either fail return $ eitherParsec input
expected' <- either fail return $ eitherParsec expected
VersionInterval.normaliseVersionRange input' @?= Right expected'
cannotNormaliseExample :: String -> ConversionProblem -> TestTree
cannotNormaliseExample input problem = testCase ("cannot " ++ input) $ do
input' <- either fail return $ eitherParsec input
VersionInterval.normaliseVersionRange input' @?= Left problem
-------------------------------------------------------------------------------
-- Intersect
-------------------------------------------------------------------------------
intersectComplete :: VersionInterval -> VersionInterval -> Version -> QC.Property
intersectComplete a b v =
QC.counterexample ("intersection: " ++ show (ab, inA, inB)) $ (inA && inB) === inAB
where
ab = intersectInterval a b
inA = withinInterval v a
inB = withinInterval v b
inAB = maybe False (withinInterval v) ab
intersectCompleteLax :: VersionInterval -> VersionInterval -> Version -> QC.Property
intersectCompleteLax a b v =
QC.counterexample ("intersection: " ++ show (ab, inA, inB)) $ (inA && inB) === inAB
where
ab = intersectInterval a b
inA = withinIntervalLax v a
inB = withinIntervalLax v b
inAB = maybe False (withinIntervalLax v) ab
-------------------------------------------------------------------------------
-- Stage 1
-------------------------------------------------------------------------------
stage1_valid :: VersionRange -> QC.Property
stage1_valid vr = QC.counterexample ("stage1: " ++ show is) $ QC.property $ all validVersionInterval is
where
is = stage1 id vr
stage1_complete :: VersionRange -> Version -> QC.Property
stage1_complete vr v = QC.counterexample ("stage1: " ++ show is) $ withinRange v vr === any (withinInterval v) is
where
is = stage1 id vr
stage1_complete_lax :: VersionRange -> Version -> QC.Property
stage1_complete_lax vr v = QC.counterexample ("stage1: " ++ show is) $ withinRangeLax v vr === any (withinIntervalLax v) is
where
is = stage1 id vr
-------------------------------------------------------------------------------
-- Stage 2
-------------------------------------------------------------------------------
stage2_valid :: [VersionInterval] -> QC.Property
stage2_valid is' = QC.counterexample ("stage2: " ++ show is) $ QC.property $ all validVersionInterval is
where
is = stage2 is'
stage2_involutive :: [VersionInterval] -> QC.Property
stage2_involutive is' = is1 === is2
where
is1 = stage2 is'
is2 = stage2 is1
stage2_complete :: [VersionInterval] -> Version -> QC.Property
stage2_complete is' v = QC.counterexample ("stage2: " ++ show is) $ any (withinInterval v) is' === any (withinInterval v) is
where
is = stage2 is'
stage2_complete_lax :: [VersionInterval] -> Version -> QC.Property
stage2_complete_lax is' v = QC.counterexample ("stage2: " ++ show is) $ any (withinIntervalLax v) is' === any (withinIntervalLax v) is
where
is = stage2 is'
-------------------------------------------------------------------------------
-- Stage 3
-------------------------------------------------------------------------------
stage3_valid :: [VersionInterval] -> QC.Property
stage3_valid is' = QC.counterexample ("stage3: " ++ show is) $ QC.property $ all validVersionInterval is
where
is = stage3 (stage2 is')
stage3_involutive :: [VersionInterval] -> QC.Property
stage3_involutive is' = is1 === is2
where
is1 = stage3 (stage2 is')
is2 = stage3 is1
stage3_complete :: [VersionInterval] -> Version -> QC.Property
stage3_complete is' v = QC.counterexample ("stage3: " ++ show is) $ any (withinInterval v) is' === any (withinInterval v) is
where
is = stage3 (stage2 is')
stage3_complete_lax :: [VersionInterval] -> Version -> QC.Property
stage3_complete_lax is' v = QC.counterexample ("stage3: " ++ show is) $ any (withinIntervalLax v) is' === any (withinIntervalLax v) is
where
is = stage3 (stage2 is')
-------------------------------------------------------------------------------
-- Normalise
-------------------------------------------------------------------------------
normaliseInvolutive :: VersionRange -> QC.Property
normaliseInvolutive vr = vr1 === vr2
where
vr1 = VersionInterval.normaliseVersionRange vr
vr2 = VersionInterval.normaliseVersionRange =<< vr1
normaliseComplete :: VersionRange -> Version -> QC.Property
normaliseComplete vr v = case VersionInterval.normaliseVersionRange vr of
Left _ -> QC.property True
Right vr' -> QC.counterexample ("normalised: " ++ show vr') $ withinRange v vr === withinRange v vr'
normaliseCompleteLax :: VersionRange -> Version -> QC.Property
normaliseCompleteLax vr v = case VersionInterval.normaliseVersionRange vr of
Left _ -> QC.property True
Right vr' -> QC.counterexample ("normalised: " ++ show vr') $ withinRangeLax v vr === withinRangeLax v vr'
-------------------------------------------------------------------------------
-- Predicates
-------------------------------------------------------------------------------
withinInterval :: Version -> VersionInterval -> Bool
withinInterval v (VI l m u) = viGreater l && viLessM m && viLess u
where
viGreater (LB v') = v >= v'
viLessM NoMB = True
viLessM (MB v') = v < v'
viLess NoUB = True
viLess (UB v') = v < v'
withinIntervalLax :: Version -> VersionInterval -> Bool
withinIntervalLax v (VI l _ u) = viGreater l && viLess u
where
viGreater (LB v') = v >= v'
viLess NoUB = True
viLess (UB v') = v < v'
withinRangeLax :: Version -> VersionRange -> Bool
withinRangeLax v vr = withinRange v (transformCaretUpper vr)
-------------------------------------------------------------------------------
-- QC instances
-------------------------------------------------------------------------------
instance Arbitrary Version where
arbitrary =
QC.oneof
[ mkVersion <$> replicateM d vDigit
| d <- [1 .. 4]
]
where
vDigit :: QC.Gen Int
vDigit = QC.elements [0 .. 9]
shrink v =
[ mkVersion v'
| v' <- shrink (versionNumbers v),
not (null v')
]
instance Arbitrary VersionRange where
arbitrary = QC.sized $ \n -> genVersionRange (intLog2 (max 1 n))
shrink vr = case projectVersionRange vr of
LaterVersionF v -> laterVersion <$> shrink v
OrLaterVersionF v -> orLaterVersion <$> shrink v
EarlierVersionF v -> earlierVersion <$> shrink v
OrEarlierVersionF v -> orEarlierVersion <$> shrink v
ThisVersionF v -> thisVersion <$> shrink v
MajorBoundVersionF v -> majorBoundVersion <$> shrink v
UnionVersionRangesF l r -> l : r : fmap (uncurry unionVersionRanges) (shrink (l, r))
IntersectVersionRangesF l r -> l : r : fmap (uncurry intersectVersionRanges) (shrink (l, r))
genVersionRange :: Int -> QC.Gen VersionRange
genVersionRange n
| n <= 1 =
QC.oneof
[ pure anyVersion,
thisVersion <$> arbitrary,
notThisVersion <$> arbitrary,
laterVersion <$> arbitrary,
earlierVersion <$> arbitrary,
orLaterVersion <$> arbitrary,
orEarlierVersion <$> arbitrary,
withinVersion <$> arbitrary,
majorBoundVersion <$> arbitrary
]
| otherwise = do
l <- QC.chooseInt (1, n - 1)
let r = n - l
QC.oneof
[ unionVersionRanges <$> genVersionRange l <*> genVersionRange r,
intersectVersionRanges <$> genVersionRange l <*> genVersionRange r
]
instance Arbitrary VersionInterval where
arbitrary = QC.suchThat (VI <$> arbitrary <*> arbitrary <*> arbitrary) validVersionInterval
shrink (VI l m u) =
[ vi
| (l', m', u') <- shrink (l, m, u),
let vi = VI l' m' u',
validVersionInterval vi
]
instance Arbitrary LB where
arbitrary = LB <$> arbitrary
shrink (LB v) = LB <$> shrink v
instance Arbitrary UB where
arbitrary =
QC.oneof
[ pure NoUB,
UB <$> arbitrary
]
shrink NoUB = []
shrink (UB v) = NoUB : map UB (shrink v)
instance Arbitrary MB where
arbitrary =
QC.oneof
[ pure NoMB,
MB <$> arbitrary
]
shrink NoMB = []
shrink (MB v) = NoMB : map MB (shrink v)