haskey-btree-0.1.0.0: tests/Integration/WriteOpenRead/Transactions.hs
{-# LANGUAGE RecordWildCards #-}
module Integration.WriteOpenRead.Transactions where
import Test.QuickCheck
import Control.Applicative ((<$>), (<*>), pure)
import Control.Monad.State
import Data.List (inits)
import Data.Map (Map)
import qualified Data.Map as M
--------------------------------------------------------------------------------
newtype TestSequence k v = TestSequence [TestTransaction k v]
deriving (Show)
data TransactionSetup = TransactionSetup { sequenceInsertFrequency :: !Int
, sequenceReplaceFrequency :: !Int
, sequenceDeleteFrequency :: !Int }
deriving (Show)
deleteHeavySetup :: TransactionSetup
deleteHeavySetup = TransactionSetup { sequenceInsertFrequency = 35
, sequenceReplaceFrequency = 20
, sequenceDeleteFrequency = 45 }
insertHeavySetup :: TransactionSetup
insertHeavySetup = TransactionSetup { sequenceInsertFrequency = 12
, sequenceReplaceFrequency = 4
, sequenceDeleteFrequency = 4 }
genTransactionSetup :: Gen TransactionSetup
genTransactionSetup =
frequency [(45, return deleteHeavySetup),
(45, return insertHeavySetup)]
newtype TestTransaction k v = TestTransaction [TestAction k v]
deriving (Show)
testTransactionResult :: Ord k => Map k v -> TestTransaction k v -> Map k v
testTransactionResult m (TestTransaction actions)
= foldl (flip doAction) m actions
data TestAction k v = Insert k v
| Replace k v
| Delete k
deriving (Show)
doAction :: Ord k => TestAction k v -> Map k v -> Map k v
doAction action m
| Insert k v <- action = M.insert k v m
| Replace k v <- action = M.insert k v m
| Delete k <- action = M.delete k m
genTestTransaction :: (Ord k, Arbitrary k, Arbitrary v) => Map k v -> TransactionSetup -> Gen (TestTransaction k v, Map k v)
genTestTransaction db TransactionSetup{..} = sized $ \n -> do
k <- choose (0, n)
(m, actions) <- execStateT (replicateM k next) (db, [])
tx <- TestTransaction <$> pure (reverse actions)
return (tx, m)
where
genAction :: (Ord k, Arbitrary k, Arbitrary v)
=> Map k v
-> Gen (TestAction k v)
genAction m
| M.null m = genInsert
| otherwise = frequency [(sequenceInsertFrequency, genInsert ),
(sequenceReplaceFrequency, genReplace m),
(sequenceDeleteFrequency, genDelete m )]
genInsert :: (Arbitrary k, Arbitrary v) => Gen (TestAction k v)
genInsert = Insert <$> arbitrary <*> arbitrary
genReplace m = Replace <$> elements (M.keys m) <*> arbitrary
genDelete m = Delete <$> elements (M.keys m)
next :: (Ord k, Arbitrary k, Arbitrary v)
=> StateT (Map k v, [TestAction k v]) Gen ()
next = do
(m, actions) <- get
action <- lift $ genAction m
put (doAction action m, action:actions)
shrinkTestTransaction :: (Ord k, Arbitrary k, Arbitrary v)
=> TestTransaction k v
-> [TestTransaction k v]
shrinkTestTransaction (TestTransaction actions) = map TestTransaction (init (inits actions))
genTestSequence :: (Ord k, Arbitrary k, Arbitrary v) => Gen (TestSequence k v)
genTestSequence = sized $ \n -> do
k <- choose (0, n)
(_, txs) <- execStateT (replicateM k next) (M.empty, [])
return $ TestSequence (reverse txs)
where
next :: (Ord k, Arbitrary k, Arbitrary v)
=> StateT (Map k v, [TestTransaction k v]) Gen ()
next = do
(m, txs) <- get
(tx, m') <- lift $ genTransactionSetup >>= genTestTransaction m
put (m', tx:txs)
shrinkTestSequence :: (Ord k, Arbitrary k, Arbitrary v)
=> TestSequence k v
-> [TestSequence k v]
shrinkTestSequence (TestSequence txs) = map TestSequence (shrinkList shrinkTestTransaction txs)