{-# LANGUAGE TupleSections #-}
import Test.HUnit
import Control.Monad
import Data.IORef
import System.Exit
import System.Random
import Data.Binding.List as B
import Prelude as P
-- Change these to exercise different variable and data types
type V = IORef
type A = Int
-- *** Test pure helpers ***
-- | Generate a list for testing.
-- Many operations are expected to fail on lists of less than 2 elements.
list' :: IO ([A], Int)
list' = do size <- randomRIO (2,100)
list <- replicateM size randomIO
return (list, size)
testRemove' :: Assertion
testRemove' = do (list, size) <- list'
pos <- randomRIO (0, size-2)
let actual = remove' list pos
assertEqual "List hasn't shrunk correctly" (size-1) (P.length actual)
assertEqual "Head of list incorrect" (take pos list) (take pos actual)
assertEqual "Tail of list incorrect" (drop (pos+1) list) (drop pos actual)
testRemoveLast' :: Assertion
testRemoveLast' = do (list, size) <- list'
let actual = remove' list (size-1)
assertEqual "List hasn't shrunk correctly" (size-1) (P.length actual)
assertEqual "List is incorrect" (take (size-1) list) actual
testInsert' :: Assertion
testInsert' = do (list, size) <- list'
pos <- randomRIO (0, size-1)
new <- randomIO
let actual = insert' list pos new
assertEqual "List hasn't shrunk correctly" (size+1) (P.length actual)
assertEqual "Head of list incorrect" (take pos list) (take pos actual)
assertEqual "Element not inserted" new (actual !! pos)
assertEqual "Tail of list incorrect" (drop pos list) (drop (pos+1) actual)
--- *** Test monadic functions ***
testSource :: Assertion
testSource = do --bind a source
expected <- randomIO
source <- newVar expected :: IO (Source V A)
target <- randomIO >>= newVar :: IO (Source V A)
bind source id target writeVar
actual <- readVar target
assertEqual "Initial Bind" expected actual
--change its value
expected <- randomIO
writeVar source expected
actual <- readVar target
assertEqual "Value Changed" expected actual
-- | Generate a 'BindingList' for testing.
list :: IO ([A], Int, BindingList V A)
list = do (list, size) <- list'
liftM (list, size,) (toBindingList list)
-- | Assert that a 'BindingList' holds the expected list.
assertList :: [A] -> BindingList V A -> Assertion
assertList list bl = fromBindingList bl >>= (list @=?)
-- | Assert that a 'BindingList' holds the expected list.
assertPos :: Int -> BindingList V A -> Int -> Assertion
assertPos expected bl reported = do pos <- position bl
assertEqual "Wrong positon" expected pos
assertEqual "Wrong positon reported" pos reported
testList :: Assertion
testList = do (expected, _, bl) <- list
assertList expected bl
testLength :: Assertion
testLength = do (_, expected, bl) <- list
B.length bl >>= (expected @=?)
testSeek :: Assertion
testSeek = do (list, size, bl) <- list
pos <- randomRIO (0,size-1)
seek bl pos >>= assertPos pos bl
actual <- readVar bl
list !! pos @=? actual
testSeekBy :: Assertion
testSeekBy = do (_, size, bl) <- list
init <- randomRIO (0, size-1)
offset <- randomRIO (-init, size-init-1)
let expected = init + offset
seek bl init
actual <- seekBy (offset+) bl
--give a more detailed error message than assertPos
assertEqual ("Seek from " ++ show init ++ " by " ++ show offset) expected actual
assertPos expected bl actual
testNext :: Assertion
testNext = do (_, size, bl) <- list
init <- randomRIO (0, size-2)
seek bl init
B.next bl >>= assertPos (init+1) bl
testPrev :: Assertion
testPrev = do (_, size, bl) <- list
init <- randomRIO (1, size-1)
seek bl init
prev bl >>= assertPos (init-1) bl
testRemove :: Assertion
testRemove = do (list, size, bl) <- list
pos <- randomRIO (0, size-2)
seek bl pos
remove bl >>= assertPos pos bl
assertList (remove' list pos) bl
testRemoveLast :: Assertion
testRemoveLast = do (list, size, bl) <- list
seek bl (size-1)
remove bl >>= assertPos (size-2) bl
assertList (remove' list (size-1)) bl
testInsert :: Assertion
testInsert = do (list, size, bl) <- list
pos <- randomRIO (0, size-1)
new <- randomIO
seek bl pos
let pos' = pos+1
insert bl new >>= assertPos pos' bl
assertList (insert' list pos' new) bl
main = do Counts _ _ e f <- runTestTT $ TestList
["Source" ~: testSource
,"binding lists" ~: testList
,"length" ~: testLength
,"seek" ~: testSeek
,"seekBy" ~: testSeekBy
,"next" ~: testNext
,"prev" ~: testPrev
,"remove'" ~: testRemove'
,"remove" ~: testRemove
,"remove' last" ~: testRemoveLast'
,"remove last" ~: testRemoveLast
,"insert'" ~: testInsert'
,"insert" ~: testInsert]
when (e>0 || f>0) exitFailure