sindre-0.1: tests/Properties.hs
{-# LANGUAGE FlexibleInstances #-}
module Properties where
import Sindre.Sindre
import Test.QuickCheck
import Text.Printf
import Control.Applicative
import Control.Monad
import Data.Monoid
main :: IO ()
main = mapM_ (\(s,a) -> putStr (s++": ") >> a) tests
instance Arbitrary Rectangle where
arbitrary = pure Rectangle
<*> choose (0,1000) <*> choose (0,1000)
<*> choose (0,1000) <*> choose (0,1000)
-- Transposing a rectangle twice is the same as identity.
prop_transposetranspose :: Rectangle -> Bool
prop_transposetranspose r =
(rectTranspose . rectTranspose) r == r
prop_rectangle_mempty :: Rectangle -> Bool
prop_rectangle_mempty r =
r `mappend` mempty == r && mempty `mappend` r == r
prop_rectangle_mappend_associative :: Rectangle -> Rectangle -> Rectangle -> Bool
prop_rectangle_mappend_associative r1 r2 r3 =
r1 `mappend` (r2 `mappend` r3) == (r1 `mappend` r2) `mappend` r3
prop_rectangle_mappend_idempotent :: Rectangle -> Rectangle -> Bool
prop_rectangle_mappend_idempotent r1 r2 =
r1 `mappend` r2 `mappend` r2 == r1 `mappend` r2 &&
r1 `mappend` r2 `mappend` r1 == r1 `mappend` r2 &&
r2 `mappend` r1 `mappend` r1 == r1 `mappend` r2 &&
r2 `mappend` r1 `mappend` r2 == r1 `mappend` r2
instance Arbitrary DimNeed where
arbitrary = oneof [ liftM Min (choose (0,100))
, liftM Max (choose (0,100))
, return Unlimited
, liftM Exact (choose (0,100)) ]
newtype BigEnoughDim = BigEnoughDim ([DimNeed], Integer)
deriving (Show)
-- The dimension is guaranteed to be able to satisfy the requirements.
instance Arbitrary BigEnoughDim where
arbitrary = do needs <- arbitrary
let (x1,x2) = foldl (\(x1,y1) (x2,y2) -> (x1+x2,y1+y2)) (0,0)
$ map range needs
dim <- choose (x1, x2)
return $ BigEnoughDim (needs, dim)
where range (Min x) = (x,2*x)
range (Max x) = (0,x)
range (Exact x) = (x,x)
range Unlimited = (0,100)
satisfied :: DimNeed -> Integer -> Bool
satisfied (Min x) y = x <= y
satisfied (Max x) y = x >= y
satisfied (Exact x) y = x == y
satisfied Unlimited _ = True
prop_hsplit_satisfies :: BigEnoughDim -> Bool
prop_hsplit_satisfies (BigEnoughDim (needs, dim)) =
length needs == length rs &&
all (uncurry satisfied) (zip needs $ map rectHeight rs)
where rs = splitHoriz (Rectangle 0 0 10 dim) needs
prop_hsplit_union :: [DimNeed] -> Rectangle -> Bool
prop_hsplit_union needs r =
length needs == length rs && (null needs || mconcat rs == r)
where rs = splitHoriz r needs
prop_vsplit_satisfies :: BigEnoughDim -> Bool
prop_vsplit_satisfies (BigEnoughDim (needs, dim)) =
length needs == length rs &&
all (uncurry satisfied) (zip needs $ map rectWidth rs)
where rs = splitVert (Rectangle 0 0 dim 10) needs
prop_vsplit_union :: [DimNeed] -> Rectangle -> Bool
prop_vsplit_union needs r =
length needs == length rs && (null needs || mconcat rs == r)
where rs = splitVert r needs
prop_constrains_idempotent :: SpaceNeed -> Constraints -> Bool
prop_constrains_idempotent s c =
constrainNeed (constrainNeed s c) c == constrainNeed s c
prop_fitRect_idempotent :: Rectangle -> SpaceNeed -> Bool
prop_fitRect_idempotent r s =
fitRect (fitRect r s) s == fitRect r s
prop_fitRect_subrect :: Rectangle -> SpaceNeed -> Bool
prop_fitRect_subrect r s =
fitRect r s `mappend` r == r
prop_fitRect_fits :: Rectangle -> SpaceNeed -> Bool
prop_fitRect_fits r s = check rectWidth (fst s) && check rectHeight (snd s)
where check f (Exact x) | x <= f r = f (fitRect r s) == x
| otherwise = f (fitRect r s) == f r
check f (Min x) | x <= f r = f (fitRect r s) >= x
| otherwise = f (fitRect r s) == f r
check f (Max x) = f (fitRect r s) <= x
check _ Unlimited = True
instance Arbitrary Align where
arbitrary = elements [AlignCenter, AlignNeg, AlignPos]
prop_align_fits :: Align -> Property
prop_align_fits a = do
minp <- arbitrary `suchThat` (>=(0::Integer))
d <- arbitrary `suchThat` (>=0)
maxp <- arbitrary `suchThat` (>=d+minp)
let d' = align a minp d maxp
d' >= minp .&. d' <= maxp .&. d'+d <= maxp .&.
case a of AlignCenter -> abs ((d'-minp)-(maxp-d'-d)) <= 1
AlignNeg -> d'==minp
AlignPos -> d'==maxp-d
tests :: [(String, IO ())]
tests = [ ( "Transposing twice is identity"
, quickCheck prop_transposetranspose)
, ( "Rectangle mempty is identity"
, quickCheck prop_rectangle_mempty)
, ( "Rectangle mappend is associative"
, quickCheck prop_rectangle_mappend_associative)
, ( "Rectangle mappend is idempotent"
, quickCheck prop_rectangle_mappend_idempotent)
, ( "Horizontal split fulfills constraints"
, quickCheck prop_hsplit_satisfies )
, ( "Union is inverse of horizontal split"
, quickCheck prop_hsplit_union )
, ( "Vertical split fulfills constraints"
, quickCheck prop_vsplit_satisfies )
, ( "Union is inverse of vertical split"
, quickCheck prop_vsplit_union )
, ( "Constraining is idempotent"
, quickCheck prop_constrains_idempotent )
, ( "Rectangle fitting is idempotent"
, quickCheck prop_fitRect_idempotent )
, ( "Fitted rectangle is a subrectangle"
, quickCheck prop_fitRect_subrect )
, ( "Rectangle fitting fits"
, quickCheck prop_fitRect_fits )
, ( "Aligning fits and cannot be improved"
, quickCheck prop_align_fits)
]