packages feed

powerqueue-levelmem-0.1.0.0: test/Data/PowerQueue/Backend/LevelMemSpec.hs

module Data.PowerQueue.Backend.LevelMemSpec
    ( spec )
where

import Data.PowerQueue
import Data.PowerQueue.Backend.LevelMem

import Control.Concurrent.Async
import Control.Monad
import Data.IORef
import Data.List
import System.IO.Temp
import Test.Hspec
import qualified Data.Serialize as S

dummyRefWorker :: (a -> b -> b) -> IORef b -> QueueWorker a
dummyRefWorker f ioRef =
    newQueueWorker $ \v -> atomicModifyIORef' ioRef $ \i -> (f v i, JOk)

dummyRefQueue :: QueueBackend a ->  (a -> b -> b) -> IORef b -> Queue a
dummyRefQueue be f ioRef =
    newQueue be (dummyRefWorker f ioRef)

dummyCounterQueue :: QueueBackend () -> IORef Int -> Queue ()
dummyCounterQueue be = dummyRefQueue be (\_ i -> i + 1)

dummyReverseAccumQueue :: QueueBackend Int -> IORef [Int] -> Queue Int
dummyReverseAccumQueue be = dummyRefQueue be (:)

binEncoding :: S.Serialize a => JobEncoding a
binEncoding =
    JobEncoding
    { j_encode = S.encode
    , j_decode = S.decode
    }

withTmpDir :: (FilePath -> IO a) -> IO a
withTmpDir = withSystemTempDirectory "lmstestsXXX"

testCfg ::  S.Serialize a => FilePath -> LevelMemCfg a
testCfg fp =
    LevelMemCfg
    { lmc_storageDir = fp
    , lmc_maxQueueSize = 100
    , lmc_jobEncoding = binEncoding
    , lmc_inProgressRecovery = IpRestart
    }

spec :: Spec
spec =
    describe "Level Mem Backend" $
    do it "providing 100 jobs and stepping 100 times works" $
           withTmpDir $ \tmpDir ->
           withLevelMem (testCfg tmpDir) $ \lm ->
           do ref <- newIORef 0
              let queue = dummyCounterQueue (newLevelMemBackend lm) ref
              replicateM_ 100 $ enqueueJob () queue
              replicateM_ 100 $ workStep queue
              result <- readIORef ref
              result `shouldBe` 100
       it "synchronous recovery works" $
           withTmpDir $ \tmpDir ->
           do ref <- newIORef 0
              withLevelMem (testCfg tmpDir) $ \lm ->
                  do let queue = dummyCounterQueue (newLevelMemBackend lm) ref
                     replicateM_ 100 $ enqueueJob () queue
                     replicateM_ 50 $ workStep queue
              withLevelMem (testCfg tmpDir) $ \lm ->
                  do statusMap <- getJobStatusMap lm
                     length statusMap `shouldBe` 50
                     let queue = dummyCounterQueue (newLevelMemBackend lm) ref
                     replicateM_ 50 $ workStep queue
              result <- readIORef ref
              result `shouldBe` 100
       it "asynchronous working and recovery works" $
           withTmpDir $ \tmpDir ->
           do ref <- newIORef []
              withLevelMem (testCfg tmpDir) $ \lm ->
                  do let queue = dummyReverseAccumQueue (newLevelMemBackend lm) ref
                     adder <- async $ forConcurrently_ [1..100] $ \idx -> enqueueJob idx queue
                     worker <- async $ replicateConcurrently_ 10 $ replicateM_ 5 $ workStep queue
                     wait adder
                     wait worker
              withLevelMem (testCfg tmpDir) $ \lm ->
                  do statusMap <- getJobStatusMap lm
                     length statusMap `shouldBe` 50
                     let queue = dummyReverseAccumQueue (newLevelMemBackend lm) ref
                     replicateM_ 50 $ workStep queue
              result <- sort <$> readIORef ref
              result `shouldBe` [1..100]