binding-core-0.2.1: tests/QuickCheck.hs
{-# LANGUAGE TupleSections, TemplateHaskell #-}
import Test.QuickCheck
import Test.QuickCheck.Modifiers
import Test.QuickCheck.Monadic
import Test.QuickCheck.All
import Test.QuickCheck.Test
import Control.Monad
import Data.IORef
import System.Exit
import Data.Binding.List as B
import Prelude as P
-- Change these to exercise different variable and data types
type V = IORef
type A = Char
-- *** Functions to generate lists and pointers ***
-- | A random list with at least two elements.
newtype List = List [A] deriving Show
instance Arbitrary List where
arbitrary = do a <- arbitrary
b <- arbitrary
c <- arbitrary
return $ List (a:b:c)
shrink (List [a,b]) = [List [a',b'] | a' <- shrink a, b' <- shrink a]
shrink (List xs) = map List $ shrink xs
-- | Maps i to a pointer in @xs@.
anywhere :: Int -> [A] -> Int
anywhere i xs = let max = P.length xs - 1
in if max == 0 then 0 else i `mod` max
-- | Anywhere in the list except the last element.
notLast :: Int -> [A] -> Int
notLast i = anywhere i . tail
-- *** Test pure functions ***
prop_remove' :: [A] -> Int -> Bool
prop_remove' xs i = let pos = anywhere i xs
actual = remove' xs pos
in P.length actual == P.length xs - 1
&& take pos actual == take pos xs
&& drop (pos+1) xs == drop pos actual
prop_removeLast' :: [A] -> Bool
prop_removeLast' xs = let pos = P.length xs - 1
actual = remove' xs pos
in P.length actual == pos
&& actual == take pos xs
prop_insert' :: [A] -> Int -> A -> Bool
prop_insert' xs i x = let pos = anywhere i xs
actual = insert' xs pos x
in P.length actual == P.length xs + 1
&& take pos actual == take pos xs
&& actual !! pos == x
&& drop pos actual == drop (pos+1) xs
-- *** Tests in 'IO'. They are converted to 'Property's with 'monadicIO'. ***
-- | Create a 'BindingList', and 'seek' to @pos@.
list :: [A] -> Int -> IO (BindingList V A)
list xs pos = do bl <- toBindingList xs
seek bl pos
return bl
testSource :: (A,A,A) -> IO (A,A)
testSource (a,b,c) = do --bind a source
source <- newVar a :: IO (Source V A)
target <- newVar c :: IO (Source V A)
bind source id target writeVar
x <- readVar target
--change its value
writeVar source b
y <- readVar target
return (x,y)
testSeek :: [A] -> Int -> IO (Int, A)
testSeek xs pos = do bl <- toBindingList xs :: IO (BindingList V A)
liftM2 (,) (seek bl pos) (readVar bl)
testPosition :: [A] -> Int -> IO Int
testPosition xs init = list xs init >>= position
testSeekBy :: [A] -> Int -> Int -> IO (Int, A)
testSeekBy xs init offset = do bl <- list xs init
liftM2 (,) (seekBy (offset+) bl) (readVar bl)
testNext :: [A] -> Int -> IO (Int, A)
testNext xs init = do bl <- list xs init
liftM2 (,) (B.next bl) (readVar bl)
testPrev :: [A] -> Int -> IO (Int, A)
testPrev xs init = do bl <- list xs init
liftM2 (,) (prev bl) (readVar bl)
testRemove :: [A] -> Int -> IO (Int, [A])
testRemove xs pos = do bl <- list xs pos
liftM2 (,) (remove bl) (fromBindingList bl)
testInsert :: [A] -> Int -> A -> IO (Int, [A])
testInsert xs pos new = do bl <- list xs pos
liftM2 (,) (insert bl new) (fromBindingList bl)
-- *** QuickCheck 'Property's for Monadic actions. ***
prop_Source :: (A,A,A) -> Property
prop_Source (a,b,c) = monadicIO $ do
(x,y) <- run $ testSource (a,b,c)
assert (x==a && y==b)
prop_Length :: NonEmptyList A -> Property
prop_Length (NonEmpty xs) = monadicIO $ do
l <- run $ (toBindingList xs :: IO (BindingList V A)) >>= B.length
assert (l == P.length xs)
prop_seek :: NonEmptyList A -> Int -> Property
prop_seek (NonEmpty xs) i = let pos = anywhere i xs in monadicIO $ do
(new, x) <- run $ testSeek xs pos
assert (new == pos && x == xs !! pos)
prop_position :: NonEmptyList A -> Int -> Property
prop_position (NonEmpty xs) i = let pos = anywhere i xs in monadicIO $ do
new <- run $ testPosition xs pos
assert (new == pos)
prop_seekBy :: List -> Int -> Int -> Property
prop_seekBy (List xs) a b = let size = P.length xs
init = anywhere a xs
offset = anywhere b xs - init
in monadicIO $ do
(new, x) <- run $ testSeekBy xs init offset
assert (new == init + offset && x == xs !! new)
prop_next :: List -> Int -> Property
prop_next (List xs) i = let pos = notLast i xs in monadicIO $ do
(new, x) <- run $ testNext xs pos
assert (new == pos + 1 && x == xs !! new)
prop_prev :: List -> Int -> Property
prop_prev (List xs) i = let pos = anywhere i xs + 1 in monadicIO $ do
(new, x) <- run $ testPrev xs pos
assert (new == pos - 1 && x == xs !! new)
prop_remove :: List -> Int -> Property
prop_remove (List xs) i = let pos = notLast i xs in monadicIO $ do
(pos',ys) <- run $ testRemove xs pos
assert (ys == remove' xs pos && pos' == pos)
prop_removeLast :: List -> Property
prop_removeLast (List xs) = let pos = P.length xs - 1 in monadicIO $ do
(pos',ys) <- run $ testRemove xs pos
assert (ys == remove' xs pos && pos' == pos -1)
prop_insert :: List -> Int -> A -> Property
prop_insert (List xs) i x = let pos = anywhere i xs
new = pos + 1
in monadicIO $ do
(pos',ys) <- run $ testInsert xs pos x
assert (ys == insert' xs new x && pos' == new)
-- | Test the 'Property's.
main = do passed <- $quickCheckAll
unless passed exitFailure