{-# OPTIONS_GHC -F -pgmF htfpp #-} {-# LANGUAGE TupleSections #-} module HUnit where import Test.Framework import Test.HUnit.Lang import Control.Monad import Data.IORef import System.Random 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 = 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) test_Remove' = do (list, size) <- list' pos <- randomRIO (0, size-2) let actual = remove' list pos assertEqualVerbose "List hasn't shrunk correctly" (size-1) (P.length actual) assertEqualVerbose "Head of list incorrect" (take pos list) (take pos actual) assertEqualVerbose "Tail of list incorrect" (drop (pos+1) list) (drop pos actual) test_RemoveLast' :: Assertion test_RemoveLast' = do (list, size) <- list' let actual = remove' list (size-1) assertEqualVerbose "List hasn't shrunk correctly" (size-1) (P.length actual) assertEqualVerbose "List is incorrect" (take (size-1) list) actual test_Insert' :: Assertion test_Insert' = do (list, size) <- list' pos <- randomRIO (0, size-1) new <- randomIO let actual = insert' list pos new assertEqualVerbose "List hasn't shrunk correctly" (size+1) (P.length actual) assertEqualVerbose "Head of list incorrect" (take pos list) (take pos actual) assertEqualVerbose "Element not inserted" new (actual !! pos) assertEqualVerbose "Tail of list incorrect" (drop pos list) (drop (pos+1) actual) --- *** Test monadic functions *** test_Source :: Assertion test_Source = 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 assertEqualVerbose "Initial Bind" expected actual --change its value expected <- randomIO writeVar source expected actual <- readVar target assertEqualVerbose "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 >>= assertEqual list -- | Assert that a 'BindingList' holds the expected list. assertPos :: Int -> BindingList V A -> Int -> Assertion assertPos expected bl reported = do pos <- position bl assertEqualVerbose "Wrong positon" expected pos assertEqualVerbose "Wrong positon reported" pos reported test_List :: Assertion test_List = do (expected, _, bl) <- list assertList expected bl test_Length :: Assertion test_Length = do (_, expected, bl) <- list B.length bl >>= assertEqual expected test_Seek :: Assertion test_Seek = do (list, size, bl) <- list pos <- randomRIO (0,size-1) seek bl pos >>= assertPos pos bl actual <- readVar bl assertEqual (list !! pos) actual test_SeekBy :: Assertion test_SeekBy = 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 assertEqualVerbose ("Seek from " ++ show init ++ " by " ++ show offset) expected actual assertPos expected bl actual test_Next :: Assertion test_Next = do (_, size, bl) <- list init <- randomRIO (0, size-2) seek bl init B.next bl >>= assertPos (init+1) bl test_Prev :: Assertion test_Prev = do (_, size, bl) <- list init <- randomRIO (1, size-1) seek bl init prev bl >>= assertPos (init-1) bl test_Remove :: Assertion test_Remove = do (list, size, bl) <- list pos <- randomRIO (0, size-2) seek bl pos remove bl >>= assertPos pos bl assertList (remove' list pos) bl test_RemoveLast :: Assertion test_RemoveLast = do (list, size, bl) <- list seek bl (size-1) remove bl >>= assertPos (size-2) bl assertList (remove' list (size-1)) bl test_Insert :: Assertion test_Insert = 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