binding-core-0.2.2: tests/QuickCheck.hs
{-# OPTIONS_GHC -F -pgmF htfpp #-}
{-# LANGUAGE TupleSections #-}
module QuickCheck where
import Test.Framework
import Test.QuickCheck.Monadic
import Control.Monad
import Data.IORef
import Data.Binding.List as B
import Prelude as P
{-# ANN module "HLint: ignore Use camelCase" #-}
-- Change these to exercise different variable and data types
type V = IORef
type A = Char
-- *** Helpers to generate random lists and positions ***
-- | A random list with at least two elements.
newtype List = List [A] deriving Show
instance Arbitrary List where
arbitrary = liftM List $ choose (2, 100) >>= vector
shrink (List xs) = [List ys | ys <- shrink xs, P.length ys > 1]
-- | Maps @i@ to a position 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
-- | 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
-- *** Test pure functions ***
prop_remove' :: NonEmptyList A -> Int -> Bool
prop_remove' (NonEmpty 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' :: NonEmptyList A -> Bool
prop_removeLast' (NonEmpty xs) = let pos = P.length xs - 1
actual = remove' xs pos
in P.length actual == pos
&& actual == take pos xs
prop_insert' :: NonEmptyList A -> Int -> A -> Bool
prop_insert' (NonEmpty 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 xs == drop (pos+1) actual
-- *** QuickCheck 'Property's for Monadic actions. ***
prop_Source :: (A,A,A) -> Property
prop_Source (a,b,c) = monadicIO $ do
(x,y) <- run $ 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)
assert (x==a && y==b)
prop_List :: NonEmptyList A -> Property
prop_List (NonEmpty xs) = monadicIO $ do
ys <- run $ (toBindingList xs :: IO (BindingList V A)) >>= fromBindingList
assert (ys == xs)
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 $ do bl <- toBindingList xs :: IO (BindingList V A)
liftM2 (,) (seek bl pos) (readVar bl)
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 $ list xs pos >>= position
assert (new == pos)
prop_seekBy :: NonEmptyList A -> Int -> Int -> Property
prop_seekBy (NonEmpty xs) a b = let init = anywhere a xs
offset = anywhere b xs - init
in monadicIO $ do
(new, x) <- run $ do bl <- list xs init
liftM2 (,) (seekBy (offset+) bl) (readVar bl)
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 $ do bl <- list xs pos
liftM2 (,) (B.next bl) (readVar bl)
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 $ do bl <- list xs pos
liftM2 (,) (prev bl) (readVar bl)
assert (new == pos - 1 && x == xs !! new)
prop_insert :: NonEmptyList A -> Int -> A -> Property
prop_insert (NonEmpty xs) i x = let pos = anywhere i xs
new = pos + 1
in monadicIO $ do
(pos', ys) <- run $ do bl <- list xs pos
liftM2 (,) (insert bl x) (fromBindingList bl)
assert (ys == insert' xs new x && pos' == new)
-- we test removing the last element separately because it's a special case
testRemove :: [A] -> Int -> PropertyM IO (Int, [A])
testRemove xs pos = run $ do bl <- list xs pos
liftM2 (,) (remove bl) (fromBindingList bl)
prop_remove :: List -> Int -> Property
prop_remove (List xs) i = let pos = notLast i xs in monadicIO $ do
(pos', ys) <- 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) <- testRemove xs pos
assert (ys == remove' xs pos && pos' == pos -1)