summaryrefslogtreecommitdiff
path: root/testsuite/tests/programs/maessen-hashtab/HashTest.hs
diff options
context:
space:
mode:
Diffstat (limited to 'testsuite/tests/programs/maessen-hashtab/HashTest.hs')
-rw-r--r--testsuite/tests/programs/maessen-hashtab/HashTest.hs278
1 files changed, 278 insertions, 0 deletions
diff --git a/testsuite/tests/programs/maessen-hashtab/HashTest.hs b/testsuite/tests/programs/maessen-hashtab/HashTest.hs
new file mode 100644
index 0000000000..51c60c0640
--- /dev/null
+++ b/testsuite/tests/programs/maessen-hashtab/HashTest.hs
@@ -0,0 +1,278 @@
+{- Test code for Data.HashTable -}
+
+module Main(main) where
+
+import Prelude hiding (lookup)
+import qualified Prelude (lookup)
+import Data.Maybe(isJust,isNothing)
+import Data.Int(Int32)
+import Test.QuickCheck
+import System.IO.Unsafe(unsafePerformIO)
+import Data.HashTab
+import Control.Monad(liftM2, foldM)
+import System.Random
+import System.Environment
+
+infixr 0 ==.
+infixr 0 ==~
+infixr 0 ~~
+
+type HT = HashTable Int Int
+newtype HashFun = HF {unHF :: (Int -> Int32)}
+data Empty = E {e :: (IO HT), hfe :: HashFun}
+data MkH = H {h :: (IO HT), hfh :: HashFun}
+newtype List a = L [a]
+
+data Action = Lookup Int
+ | Insert Int Int
+ | Delete Int
+ | Update Int Int
+ deriving (Show)
+
+instance Arbitrary Action where
+ arbitrary = frequency [(10,fmap Lookup arbitrary),
+ (5, liftM2 Insert arbitrary arbitrary),
+ (3, liftM2 Update arbitrary arbitrary),
+ (1, fmap Delete arbitrary)]
+ coarbitrary = error "coarbitrary Action"
+
+simA :: [Action] -> [Either Bool [Int]]
+simA = fst . foldl sim ([],[])
+ where sim :: ([Either Bool [Int]], [Action]) -> Action ->
+ ([Either Bool [Int]], [Action])
+ sim (res, past) (Lookup k) = (Right (lkup k past) : res, past)
+ sim (res, past) (Insert k v) = (res, Insert k v : past)
+ sim (res, past) (Delete k) = (res, Delete k : past)
+ sim (res, past) (Update k v) =
+ (Left (not (null l)) : res, Update k v : past)
+ where l = lkup k past
+ lkup _ [] = []
+ lkup k (Delete k' : _)
+ | k==k' = []
+ lkup k (Update k' v : _)
+ | k==k' = [v]
+ lkup k (Insert k' v : past)
+ | k==k' = v:lkup k past
+ lkup k (_ : past) = lkup k past
+
+runA :: HashFun -> [Action] -> IO [Either Bool (Maybe Int)]
+runA hf acts = do
+ ht <- new (==) (unHF hf)
+ let run res (Lookup a) = fmap (lkup res) $ lookup ht a
+ run res (Insert k v) = insert ht k v >> return res
+ run res (Delete k) = delete ht k >> return res
+ run res (Update k v) = fmap (upd res) $ update ht k v
+ lkup res m = Right m : res
+ upd res b = Left b : res
+ foldM run [] acts
+
+(~~) :: IO [Either Bool (Maybe Int)] -> [Either Bool [Int]] -> Bool
+acts ~~ sims = and $ zipWith same (unsafePerformIO acts) sims
+ where same (Left b) (Left b') = b==b'
+ same (Right Nothing) (Right []) = True
+ same (Right (Just a)) (Right xs) = a `elem` xs
+ same _ _ = False
+
+lookups :: HT -> [Int] -> IO [Maybe Int]
+lookups ht ks = mapM (lookup ht) ks
+
+instance Show HashFun where
+ showsPrec _ (HF hf) r
+ | hf 1 == 0 = "degenerate"++r
+ | otherwise = "usual"++r
+
+instance Show Empty where
+ showsPrec _ ee r = shows (hfe ee) r
+
+instance Show MkH where
+ showsPrec _ hh r = shows (hfh hh) $
+ ("; "++shows (unsafePerformIO (h hh >>= toList)) r)
+
+instance Show a => Show (List a) where
+ showsPrec _ (L l) r = shows l r
+
+instance Arbitrary HashFun where
+ arbitrary = frequency [(20,return (HF hashInt)),
+ (1,return (HF (const 0)))]
+ coarbitrary = error "coarbitrary HashFun"
+
+instance Arbitrary Empty where
+ arbitrary = fmap mkE arbitrary
+ where mkE (HF hf) = E {e = new (==) hf, hfe=HF hf}
+ coarbitrary = error "coarbitrary Empty"
+
+instance Arbitrary a => Arbitrary (List a) where
+ arbitrary = do
+ sz <- frequency [(50, sized return),
+ (1,return (4096*2)),
+ (0, return (1024*1024))]
+ resize sz $ fmap L $ sized vector
+ coarbitrary = error "coarbitrary (List a)"
+
+instance Arbitrary MkH where
+ arbitrary = do
+ hf <- arbitrary
+ L list <- arbitrary
+ let mkH act = H { h = act, hfh = hf }
+ return (mkH . fromList (unHF hf) $ list)
+ coarbitrary = error "coarbitrary MkH"
+
+(==~) :: (Eq a) => IO a -> IO a -> Bool
+act1 ==~ act2 = unsafePerformIO act1 == unsafePerformIO act2
+
+(==.) :: (Eq a) => IO a -> a -> Bool
+act ==. v = unsafePerformIO act == v
+
+notin :: (Testable a) => Int -> MkH -> a -> Property
+k `notin` hh = \prop ->
+ let f = (not . isJust . unsafePerformIO) (h hh >>= flip lookup k) in
+ f `trivial` prop
+
+prop_emptyLookup :: Empty -> Int -> Bool
+prop_emptyLookup ee k =
+ isNothing . unsafePerformIO $
+ (do mt <- e ee
+ lookup mt k)
+
+prop_emptyToList :: Empty -> Bool
+prop_emptyToList ee =
+ (do mt <- e ee
+ toList mt) ==. []
+
+prop_emptyFromList :: HashFun -> Int -> Bool
+prop_emptyFromList hf k =
+ (do mt <- new (==) (unHF hf) :: IO HT
+ lookup mt k) ==~
+ (do mt <- fromList (unHF hf) []
+ lookup mt k)
+
+prop_insert :: MkH -> Int -> Int -> Bool
+prop_insert hh k v =
+ (do ht <- h hh
+ insert ht k v
+ lookup ht k) ==. Just v
+
+prop_insertu :: MkH -> Int -> Int -> List Int -> Bool
+prop_insertu hh k v (L ks) =
+ let ks' = filter (k /=) ks in
+ (do ht <- h hh
+ insert ht k v
+ lookups ht ks') ==~
+ (do ht <- h hh
+ lookups ht ks')
+
+prop_delete :: MkH -> Int -> Property
+prop_delete hh k =
+ k `notin` hh $
+ isNothing . unsafePerformIO $
+ (do ht <- h hh
+ delete ht k
+ lookup ht k)
+
+prop_deleteu :: MkH -> Int -> List Int -> Bool
+prop_deleteu hh k (L ks) =
+ let ks' = filter (k /=) ks in
+ (do ht <- h hh
+ delete ht k
+ lookups ht ks') ==~
+ (do ht <- h hh
+ lookups ht ks')
+
+naiveUpdate :: HT -> Int -> Int -> IO ()
+naiveUpdate ht k v = do
+ delete ht k
+ insert ht k v
+
+prop_update :: MkH -> Int -> Int -> List Int -> Bool
+prop_update hh k v (L ks) =
+ (do ht <- h hh
+ _ <- update ht k v
+ lookups ht ks) ==~
+ (do ht <- h hh
+ naiveUpdate ht k v
+ lookups ht ks)
+
+prop_updatec :: MkH -> Int -> Int -> Bool
+prop_updatec hh k v =
+ (do ht <- h hh
+ _ <- update ht k v
+ lookup ht k) ==. Just v
+
+prop_updateLookup :: MkH -> Int -> Int -> Property
+prop_updateLookup hh k v =
+ k `notin` hh $
+ (do ht <- h hh
+ update ht k v) ==~
+ (do ht <- h hh
+ fmap isJust (lookup ht k))
+
+prop_simulation :: HashFun -> List Action -> Property
+prop_simulation hf (L acts) =
+ (null acts `trivial`) $
+ runA hf acts ~~ simA acts
+
+{-
+
+For "fromList" and "toList" properties we're a bit sloppy: we perform
+multiple insertions for a key (potentially) but give nor promises
+about which one we will retrieve with lookup, or what order they'll be
+returned by toList (or if they'll all be returned at all). Thus we
+insert all occurrences of a key with the same value, and do all
+checking via lookups.
+
+-}
+
+prop_fromList :: HashFun -> List Int -> List Int -> Property
+prop_fromList hf (L l) (L ks) =
+ null l `trivial`
+ let assocs = map (\t -> (t,t)) l in
+ ( do ht <- fromList (unHF hf) assocs
+ lookups ht ks) ==. (map (`Prelude.lookup` assocs) ks)
+
+prop_fromListInsert :: HashFun -> List (Int,Int) -> Int -> Int -> List Int -> Property
+prop_fromListInsert hf (L l) k v (L ks) =
+ null l `trivial`
+ (( do ht <- fromList (unHF hf) l
+ insert ht k v
+ lookups ht ks) ==~
+ ( do ht <- fromList (unHF hf) (l++[(k,v)])
+ lookups ht ks))
+
+prop_toList :: HashFun -> List Int -> List Int -> Property
+prop_toList hf (L l) (L ks) =
+ null l `trivial`
+ let assocs = map (\t -> (t,t)) l in
+ ( do ht <- fromList (unHF hf) assocs
+ lookups ht ks) ==~
+ ( do ht <- fromList (unHF hf) assocs
+ fmap (\as -> map (`Prelude.lookup` as) ks) $ toList ht )
+
+te :: (Testable a) => String -> a -> IO ()
+-- te name prop = putStrLn name >> verboseCheck prop
+te name prop = do
+ putStr name
+ check (defaultConfig{configMaxTest = 500,
+ configMaxFail = 10000,
+ configEvery = \_ _ -> "" }) prop
+
+main :: IO ()
+main = do
+ [r] <- getArgs
+ setStdGen (mkStdGen (read r :: Int))
+ sequence_ $
+ [ te "emptyLookup:" prop_emptyLookup,
+ te "emptyToList:" prop_emptyToList,
+ te "emptyFromList:" prop_emptyFromList,
+ te "insert:" prop_insert,
+ te "insertu:" prop_insertu,
+ te "delete:" prop_delete,
+ te "deleteu:" prop_deleteu,
+ te "update:" prop_update,
+ te "updatec:" prop_updatec,
+ te "updateLookup:" prop_updateLookup,
+ te "fromList:" prop_fromList,
+ te "fromListInsert:" prop_fromListInsert,
+ te "toList:" prop_toList,
+ te "simulation:" prop_simulation
+ ]
+ putStrLn "OK"