binding-core 0.2.1 → 0.2.2
raw patch · 6 files changed
+206/−237 lines, 6 filesdep +HTFdep ~basePVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: HTF
Dependency ranges changed: base
API changes (from Hackage documentation)
- Data.Binding.List: insert :: Variable v => BindingList v a -> a -> IO Int
+ Data.Binding.List: insert :: BindingList v a -> a -> IO Int
- Data.Binding.List: remove :: Variable v => BindingList v a -> IO Int
+ Data.Binding.List: remove :: BindingList v a -> IO Int
Files
- binding-core.cabal +8/−11
- src/Data/Binding/List.hs +5/−5
- src/Data/Binding/Simple.hs +5/−5
- tests/HTF.hs +8/−0
- tests/HUnit.hs +95/−106
- tests/QuickCheck.hs +85/−110
binding-core.cabal view
@@ -1,10 +1,10 @@ name: binding-core -version: 0.2.1 +version: 0.2.2 cabal-version: >= 1.9.2 license: BSD3 license-file: LICENSE author: Gideon Sireling -maintainer: haskell@accursoft.org +maintainer: code@accursoft.org homepage: http://code.accursoft.com/binding bug-reports: http://code.accursoft.com/binding/issues synopsis: Data Binding @@ -18,15 +18,12 @@ hs-source-dirs: src exposed-modules: Data.Binding.Simple, Data.Binding.List, Data.Variable -test-suite hunit - type: exitcode-stdio-1.0 - main-is: tests/HUnit.hs - build-depends: base, binding-core, random, HUnit - -test-suite quickcheck - type: exitcode-stdio-1.0 - main-is: tests/QuickCheck.hs - build-depends: base, binding-core, QuickCheck +test-suite HTF + type: exitcode-stdio-1.0 + main-is: HTF.hs + build-depends: base, binding-core, random, HTF, HUnit, QuickCheck + hs-source-dirs: tests + other-modules: HUnit, QuickCheck source-repository head type: hg
src/Data/Binding/List.hs view
@@ -9,8 +9,8 @@ -- | Binding List data BindingList v a = Variable v => BindingList {source :: Source v a -- ^ the list's binding source - , list :: v [v a] -- ^ the bound list - , pos :: v Int} -- ^ the current position + ,list :: v [v a] -- ^ the bound list + ,pos :: v Int} -- ^ the current position -- [v a] is itself in a Variable, to allow for insertions and deletions. -- | Create a binding list. @@ -45,7 +45,7 @@ bind = bind . source -- | The size of a binding list. -length :: Variable v => BindingList v a -> IO Int +length :: Variable v => BindingList v a -> IO Int length b = do list <- readVar (list b) return $ P.length list @@ -85,7 +85,7 @@ in xs ++ ys -- | Remove the current element from the list. -remove :: Variable v => BindingList v a -> IO Int +remove :: BindingList v a -> IO Int remove b@(BindingList _ list pos) = do list' <- readVar list pos' <- readVar pos writeVar list $ remove' list' pos' @@ -99,7 +99,7 @@ -- | Insert an element into the list. -- The new element is inserted after the current element. -- This allows appending, but precludes prepending. -insert :: Variable v => BindingList v a -> a -> IO Int +insert :: BindingList v a -> a -> IO Int insert b@(BindingList _ list pos) x = do update b list' <- readVar list pos' <- readVar pos
src/Data/Binding/Simple.hs view
@@ -11,7 +11,7 @@ data Binding a = forall d t. Binding (a -> d) t (t -> d -> IO ()) -- | Binding Source -data Source v a = Variable v => Source {bindings :: v [Binding a] -- ^ the source'a bindings +data Source v a = Variable v => Source {bindings :: v [Binding a] -- ^ the source's bindings ,var :: v a} -- ^ the bound variable -- | Update a single binding. @@ -20,9 +20,9 @@ -- | Update a binding source's bindings. update :: Source v a -> IO () -update (Source bindings source) = do bindings <- readVar bindings - a <- readVar source - mapM_ (update' a) bindings +update (Source bindings var) = do bindings <- readVar bindings + a <- readVar var + mapM_ (update' a) bindings instance Variable v => Variable (Source v) where newVar a = do bindings <- newVar [] @@ -41,7 +41,7 @@ -- | Sources for data binding. class Variable b => Bindable b where - -- | Create a data binding. + -- | Create a data binding bind :: b a -- ^ the binding source -> (a -> d) -- ^ a function that extracts data from the source -> t -- ^ the binding target
+ tests/HTF.hs view
@@ -0,0 +1,8 @@+{-# OPTIONS_GHC -F -pgmF htfpp #-} + +import Test.Framework + +import {-@ HTF_TESTS @-} HUnit +import {-@ HTF_TESTS @-} QuickCheck + +main = htfMain htf_importedTests
tests/HUnit.hs view
@@ -1,14 +1,20 @@+{-# OPTIONS_GHC -F -pgmF htfpp #-} {-# LANGUAGE TupleSections #-} -import Test.HUnit +module HUnit where + +import Test.Framework +import Test.HUnit.Lang + import Control.Monad import Data.IORef -import System.Exit 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 @@ -22,45 +28,44 @@ 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) +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) -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 +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 -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_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 *** -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 +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) @@ -69,86 +74,70 @@ -- | Assert that a 'BindingList' holds the expected list. assertList :: [A] -> BindingList V A -> Assertion -assertList list bl = fromBindingList bl >>= (list @=?) +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 - assertEqual "Wrong positon" expected pos - assertEqual "Wrong positon reported" pos reported - -testList :: Assertion -testList = do (expected, _, bl) <- list - assertList expected bl + assertEqualVerbose "Wrong positon" expected pos + assertEqualVerbose "Wrong positon reported" pos reported -testLength :: Assertion -testLength = do (_, expected, bl) <- list - B.length bl >>= (expected @=?) +test_List :: Assertion +test_List = do (expected, _, bl) <- list + assertList expected bl -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 +test_Length :: Assertion +test_Length = do (_, expected, bl) <- list + B.length bl >>= assertEqual expected -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 +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 -testNext :: Assertion -testNext = do (_, size, bl) <- list - init <- randomRIO (0, size-2) - seek bl init - B.next bl >>= assertPos (init+1) bl +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 -testPrev :: Assertion -testPrev = do (_, size, bl) <- list - init <- randomRIO (1, size-1) - seek bl init - prev bl >>= assertPos (init-1) bl +test_Next :: Assertion +test_Next = do (_, size, bl) <- list + init <- randomRIO (0, size-2) + seek bl init + B.next 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 +test_Prev :: Assertion +test_Prev = do (_, size, bl) <- list + init <- randomRIO (1, size-1) + seek bl init + prev bl >>= assertPos (init-1) 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 +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 -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 +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 -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+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
tests/QuickCheck.hs view
@@ -1,36 +1,33 @@-{-# LANGUAGE TupleSections, TemplateHaskell #-} -import Test.QuickCheck -import Test.QuickCheck.Modifiers +{-# OPTIONS_GHC -F -pgmF htfpp #-} +{-# LANGUAGE TupleSections #-} + +module QuickCheck where + +import Test.Framework 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 +{-# ANN module "HLint: ignore Use camelCase" #-} + -- Change these to exercise different variable and data types type V = IORef type A = Char --- *** Functions to generate lists and pointers *** +-- *** 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 = 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 + arbitrary = liftM List $ choose (2, 100) >>= vector + shrink (List xs) = [List ys | ys <- shrink xs, P.length ys > 1] --- | Maps i to a pointer in @xs@. +-- | 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 @@ -39,132 +36,110 @@ 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) +-- *** Test pure functions *** -testPrev :: [A] -> Int -> IO (Int, A) -testPrev xs init = do bl <- list xs init - liftM2 (,) (prev bl) (readVar bl) +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 -testRemove :: [A] -> Int -> IO (Int, [A]) -testRemove xs pos = do bl <- list xs pos - liftM2 (,) (remove bl) (fromBindingList bl) +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 -testInsert :: [A] -> Int -> A -> IO (Int, [A]) -testInsert xs pos new = do bl <- list xs pos - liftM2 (,) (insert bl new) (fromBindingList bl) +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 $ testSource (a,b,c) - assert (x==a && y==b) + (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_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_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 $ testSeek xs pos - assert (new == pos && x == xs !! pos) + (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 $ testPosition xs pos - assert (new == pos) + new <- run $ list xs pos >>= position + 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_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 $ testNext xs pos - assert (new == pos + 1 && x == xs !! new) + (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 $ testPrev xs pos - assert (new == pos - 1 && x == xs !! new) + (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) <- run $ testRemove xs pos - assert (ys == remove' xs pos && pos' == pos) + (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) <- 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+ (pos', ys) <- testRemove xs pos + assert (ys == remove' xs pos && pos' == pos -1)