packages feed

haskey-btree-0.3.0.0: tests/Integration/WriteOpenRead/Debug.hs

{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
module Integration.WriteOpenRead.Debug where

import Test.Framework (Test, testGroup)
import Test.Framework.Providers.QuickCheck2 (testProperty)
import Test.QuickCheck

import Control.Applicative ((<$>))
import Control.Monad
import Control.Monad.Identity
import Control.Monad.Trans.State

import Data.Binary (Binary)
import Data.Foldable (foldlM)
import Data.Map (Map)
import Data.Typeable (Typeable)
import Data.Word (Word8)
import qualified Data.Map as M

import GHC.Generics (Generic)

import Data.BTree.Alloc.Class
import Data.BTree.Alloc.Debug
import Data.BTree.Impure (Tree)
import Data.BTree.Primitives
import qualified Data.BTree.Impure as B

import Integration.WriteOpenRead.Transactions

tests :: Test
tests = testGroup "WriteOpenRead.Concurrent"
    [ testProperty "debug allocator" prop_debug_allocator
    ]

data AllocatorState k v = AllocatorState {
    allocatorStatePages :: Pages
  , allocatorStateTree :: Tree k v }

prop_debug_allocator :: Property
prop_debug_allocator = forAll genTestSequence $ \(TestSequence txs) ->
    let s = AllocatorState emptyPages B.empty
        m = runIdentity $ evalStateT (runSeq txs) s
    in
    m `seq` True
  where
    runSeq = foldlM writeReadTest M.empty

    writeReadTest :: Map Integer TestValue
                  -> TestTransaction Integer TestValue
                  -> StateT (AllocatorState Integer TestValue)
                            Identity
                            (Map Integer TestValue)
    writeReadTest m tx = do
        openAndWrite tx
        read' <- openAndRead
        let expected = testTransactionResult m tx
        if read' == M.toList expected
            then return expected
            else error $ "error:"
                    ++ "\n    after:   " ++ show tx
                    ++ "\n    expectd: " ++ show (M.toList expected)
                    ++ "\n    got:     " ++ show read'

    openAndRead = do
        pages <- gets allocatorStatePages
        tree  <- gets allocatorStateTree
        return $ evalDebug pages (readAll tree)

    openAndWrite tx = do
        pages <- gets allocatorStatePages
        tree  <- gets allocatorStateTree

        let (tree', pages') = runDebug pages (doTx tree tx)
        put AllocatorState {
            allocatorStatePages = pages'
          , allocatorStateTree = tree' }

readAll :: (AllocM m, Key k, Value v)
        => Tree k v
        -> m [(k, v)]
readAll = B.toList

doTx :: (AllocM m, Key k, Value v)
     => Tree k v
     -> TestTransaction k v
     -> m (Tree k v)
doTx tree (TestTransaction actions) =
    foldl (>=>) return (map writeAction actions) tree
  where
    writeAction (Insert k v) = B.insert k v
    writeAction (Replace k v) = B.insert k v
    writeAction (Delete k) = B.delete k

--------------------------------------------------------------------------------

-- | Value used for testing.
--
-- This value will overflow 20% of the time.
newtype TestValue = TestValue (Either Integer [Word8])
                  deriving (Eq, Generic, Show, Typeable)

instance Binary TestValue where
instance Value TestValue where

instance Arbitrary TestValue where
    arbitrary =
        TestValue <$> frequency [(80, Left <$> small), (20, Right <$> big)]
      where
        small = arbitrary
        big = arbitrary

--------------------------------------------------------------------------------