{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE ScopedTypeVariables #-} import Control.Monad import Data.IORef import Data.Maybe import qualified Data.Map.Strict as M import Test.QuickCheck as QC import Test.QuickCheck.Monadic as QC import Prelude hiding (lookup,elems) import System.IO import qualified Data.Set as Set import qualified Data.HashTable as H import Control.Concurrent.STM import Data.Hashable import Data.Dictionary import Data.Dictionary.Request import qualified Data.Vector as V hasDuplicates :: (Ord a) => [a] -> Bool hasDuplicates list = length list /= length set where set = Set.fromList list -- | Restrict the keys to be ints from small positive range newtype BoundedInt = BoundedInt Int deriving (Eq,Ord) instance Show BoundedInt where show (BoundedInt i) = show i instance Hashable BoundedInt where hashWithSalt a (BoundedInt i) = hashWithSalt a i instance QC.Arbitrary BoundedInt where arbitrary = BoundedInt <$> QC.choose (1,100) type TestMap = M.Map BoundedInt BoundedInt instance Dictionary (IORef TestMap) BoundedInt IO where runRequest (Lookup k) m = do mymap <- readIORef m case M.lookup k mymap of Nothing -> return False Just _ -> return True runRequest (Insert k a) m = atomicModifyIORef' m $ \mymap -> let s1 = M.size mymap in let mymap' = M.insert k a mymap in let s2 = M.size mymap' in (mymap',s1/=s2) runRequest (Update k a) m = atomicModifyIORef' m $ \mymap -> case M.lookup k mymap of Nothing -> (mymap,False) Just _ -> let s1 = M.size mymap in let mymap' = M.insert k a mymap in let s2 = M.size mymap' in (mymap',s1/=s2) runRequest (Delete k) m = atomicModifyIORef' m $ \mymap -> let s1 = M.size mymap in let mymap' = M.delete k mymap in let s2 = M.size mymap' in (mymap',s1/=s2) type TestChainHashTable = H.HashTable BoundedInt BoundedInt instance Dictionary TestChainHashTable BoundedInt IO where runRequest (Lookup k) s = do r <- H.lookup s k case r of Nothing -> return False Just _ -> return True runRequest (Insert k a) s = H.insert s k a runRequest (Update k a) s = H.insert s k a runRequest (Delete k) s = H.delete s k instance (QC.Arbitrary k) => QC.Arbitrary (Request k) where arbitrary = QC.oneof [ Insert <$> QC.arbitrary <*> QC.arbitrary , Lookup <$> QC.arbitrary , Delete <$> QC.arbitrary ] prop :: IORef TestMap -> TestChainHashTable -> Property prop ioref chainTable = monadicIO $ do (r :: Request BoundedInt) <- pick arbitrary run $ appendFile "request_sequence.txt" (show r ++ "\n") run $ runRequest r ioref run $ runRequest r chainTable mapAssocs <- run ( M.assocs <$> readIORef ioref) -- test if all entries in the Map are also in the Hash Table: resChain <- run $ sequence [ do r <- H.lookup chainTable k return $ (isJust r) && (fromJust r == a) | (k,a) <- mapAssocs ] list2 <- run $ atomically $ H.getAssocs chainTable -- test if there aren't any duplicates in the Hash Table: let res2 = (not . hasDuplicates) list2 mymap <- run $ readIORef ioref -- test if all entries in the Hash Table are also in the Map: let res3 = [ isJust $ M.lookup (fst k) mymap | k <- list2 ] assert $ and resChain && res2 && and res3 main :: IO () main = do ioref <- newIORef (M.empty :: M.Map BoundedInt BoundedInt) (ctable :: H.HashTable BoundedInt BoundedInt) <- H.newWithDefaults 10 writeFile "request_sequence.txt" "" quickCheckWith stdArgs{ maxSuccess = 50000 } $ prop ioref ctable print "------- Map ASSOCS List ----" print =<< (M.assocs <$> readIORef ioref) print "--------Hashtable AFTER --------" print =<< atomically (H.getAssocs ctable)