rangeset-0.0.1.0: test/RangeSet.hs
{-# LANGUAGE TypeApplications, StandaloneDeriving, DeriveGeneric, MonoLocalBinds #-}
module Main where
import Test.Tasty (testGroup, defaultMain, TestTree)
import Test.Tasty.HUnit ( testCase, (@?=) )
import Test.Tasty.QuickCheck
( listOf, chooseEnum,
(===),
(==>),
(.&&.),
property,
testProperty,
elements,
forAll,
genericShrink,
Arbitrary(arbitrary, shrink),
Property )
import Prelude hiding (null)
import Data.RangeSet
import Data.RangeSet.Internal
import Data.RangeSet.Internal.TestingUtils
import Data.List (nub, sort, intersect)
import GHC.Generics (Generic)
import Data.Word (Word8)
main :: IO ()
main = defaultMain tests
deriving instance Generic (RangeSet a)
data Digit = Zero | One | Two | Three | Four | Five | Six | Seven | Eight | Nine deriving (Ord, Eq, Enum, Bounded, Show, Generic)
instance (Arbitrary a, Enum a, Ord a) => Arbitrary (RangeSet a) where
arbitrary = fmap fromList (listOf arbitrary)
shrink = filter valid . genericShrink
instance Arbitrary Digit where
arbitrary = chooseEnum (Zero, Nine)
shrink Zero = []
shrink n = [Zero .. pred n]
tests :: TestTree
tests = testGroup "RangeSet" [
testProperty "arbitrary RangeSets should be valid" $ valid @Word,
emptyTests,
memberTests,
insertTests,
deleteTests,
fromListTests,
testProperty "elems and unelems shoudld be disjoint" $ elemUnelemDisjoint @Word8,
testProperty "complement . complement = id" $ complementInverse @Digit,
testProperty "unelems == elems . complement" $ complementElemsInverse @Digit,
testProperty "findMin should find the minimum" $ findMinMinimum @Word,
testProperty "findMax should find the maximum" $ findMaxMaximum @Int,
testProperty "allLess should find everything strictly less than a value" $ allLessMin @Word,
testProperty "allMore should find everything strictly more than a value" $ allMoreMax @Word,
testProperty "union should union" $ uncurry (unionProperty @Int),
testProperty "intersection should intersect" $ uncurry (intersectionProperty @Digit),
testProperty "difference should differentiate" $ uncurry (differenceProperty @Word)
]
emptyTests :: TestTree
emptyTests = testGroup "empty should" [
testCase "be null" $ null empty @?= True,
testCase "have size 0" $ size @Int empty @?= 0
]
-- member, notMember
memberTests :: TestTree
memberTests = testGroup "member should" [
testCase "work when out of range" $ notMember 5 (fromRanges [(0, 4), (6, 9)]) @?= True,
testCase "work when in range" $ member 5 (fromRanges [(0, 9)]) @?= True,
testCase "work for exact" $ member 5 (fromRanges [(5, 5)]) @?= True,
testProperty "perform like elem on elems" $ uncurry (memberElemProperty @Word)
]
-- insert
insertTests :: TestTree
insertTests =
let t = fromList [6, 2, 7, 1, 5] -- 1-2, 5-7
in testGroup "insert should" [
testCase "add something in" $ member 3 (insert 3 t) @?= True,
testCase "not affect membership for other items" $ member 4 (insert 3 t) @?= False,
testCase "not remove membership" $ member 5 (insert 4 (insert 3 t)) @?= True
]
-- delete
deleteTests :: TestTree
deleteTests =
let t = fromList [6, 2, 7, 1, 5] -- 1-2, 5-7
in testGroup "delete should" [
testCase "remove an element" $ notMember 2 (delete 2 t) @?= True,
testCase "not affect membership for other items" $ member 1 (delete 2 t) @?= True,
testCase "produce valid trees" $ all valid (scanr delete t (sort (elems t))) @?= True
]
fromListTests :: TestTree
fromListTests = testGroup "fromList" [
testProperty "should compose with elems to form (sort . nub)" $ nubSortProperty @Int,
testProperty "specifically, case 1" $ nubSortProperty [2,0,3,4,2,6],
testProperty "specifically, case 2" $ nubSortProperty [6,7,4,0,6,10,2,12,8]
]
findMinMinimum :: (Ord a, Show a, Enum a) => RangeSet a -> Property
findMinMinimum t = findMin t === safeMinimum (elems t)
where
safeMinimum [] = Nothing
safeMinimum xs = Just $ minimum xs
findMaxMaximum :: (Ord a, Show a, Enum a) => RangeSet a -> Property
findMaxMaximum t = findMax t === safeMaximum (elems t)
where
safeMaximum [] = Nothing
safeMaximum xs = Just $ maximum xs
nubSortProperty :: (Enum a, Ord a, Show a) => [a] -> Property
nubSortProperty xs = sort (nub xs) === elems (fromList xs)
memberElemProperty :: (Enum a, Ord a, Show a) => a -> RangeSet a -> Property
memberElemProperty x t = member x t === elem x (elems t)
elemUnelemDisjoint :: (Enum a, Bounded a, Eq a, Show a) => RangeSet a -> Property
elemUnelemDisjoint t = intersect (elems t) (unelems t) === []
complementInverse :: (Enum a, Bounded a, Ord a, Show a) => RangeSet a -> Property
complementInverse t = elems (complement (complement t)) === elems t
complementElemsInverse :: (Enum a, Bounded a, Ord a, Show a) => RangeSet a -> Property
complementElemsInverse t = unelems t === elems (complement t)
unionProperty :: (Ord a, Enum a, Show a) => RangeSet a -> RangeSet a -> Property
unionProperty t1 t2 = not (null t1 && null t2) ==>
forAll (elements (elems t1 ++ elems t2)) (\x ->
member x (t1 `union` t2))
.&&. valid (t1 `union` t2)
intersectionProperty :: (Ord a, Enum a, Show a) => RangeSet a -> RangeSet a -> Property
intersectionProperty t1 t2 = not (null t1 && null t2) ==>
forAll (elements (elems t1 ++ elems t2)) (\x ->
(member x t1 && member x t2) === member x (t1 `intersection` t2))
.&&. valid (t1 `intersection` t2)
differenceProperty :: (Ord a, Enum a, Show a) => RangeSet a -> RangeSet a -> Property
differenceProperty t1 t2 = not (null t1 && null t2) ==>
forAll (elements (elems t1 ++ elems t2)) (\x ->
(member x t1 && not (member x t2)) === member x (t1 `difference` t2))
.&&. valid (t1 `difference` t2)
allLessMin :: (Ord a, Enum a, Show a) => RangeSet a -> a -> Property
allLessMin t x = allLess x t === fromList (filter (< x) (elems t))
allMoreMax :: (Ord a, Enum a, Show a) => RangeSet a -> a -> Property
allMoreMax t x = allMore x t === fromList (filter (> x) (elems t))
{-
fromRanges, insertRange
-}