powerqueue-0.1.0.0: test/Data/PowerQueueSpec.hs
module Data.PowerQueueSpec
( spec )
where
import Data.PowerQueue
import Control.Concurrent.Async
import Control.Concurrent.STM
import Control.Monad
import Data.IORef
import Test.Hspec
dummyRefWorker :: (a -> b -> b) -> IORef b -> QueueWorker a
dummyRefWorker f ioRef =
newQueueWorker $ \v -> atomicModifyIORef' ioRef $ \i -> (f v i, JOk)
dummyRefQueue :: (a -> b -> b) -> IORef b -> IO (Queue a)
dummyRefQueue f ioRef =
do be <- basicChanBackend
pure $ newQueue be (dummyRefWorker f ioRef)
dummyCounterQueue :: IORef Int -> IO (Queue ())
dummyCounterQueue = dummyRefQueue (\_ i -> i + 1)
dummyStmCounterQueue :: TVar Int -> IO (Queue ())
dummyStmCounterQueue tv =
do be <- basicChanBackend
pure $ newQueue be $ newQueueWorker $ \() ->
do atomically $ modifyTVar' tv (+1)
pure JOk
dummyReverseAccumQueue :: IORef [Int] -> IO (Queue Int)
dummyReverseAccumQueue = dummyRefQueue (:)
spec :: Spec
spec =
do specCore
specLocalWorker
specLocalWorker :: Spec
specLocalWorker =
describe "localQueueWorker" $
it "should work with 100 jobs" $
do ref <- atomically $ newTVar 0
queue <- dummyStmCounterQueue ref
handler <- async $ localQueueWorker (LocalWorkerConfig 10) queue
replicateM_ 100 $ enqueueJob () queue `shouldReturn` True
result <-
atomically $
do val <- readTVar ref
when (val /= 100) retry
pure val
cancel handler
result `shouldBe` 100
specCore :: Spec
specCore =
describe "PowerQueue Core" $
do it "providing 100 jobs and stepping 100 times works" $
do ref <- newIORef 0
queue <- dummyCounterQueue ref
replicateM_ 100 $ enqueueJob () queue `shouldReturn` True
replicateM_ 100 $ workStep queue
result <- readIORef ref
result `shouldBe` 100
it "all jobs are executed" $
do ref <- newIORef []
queue <- dummyReverseAccumQueue ref
let out = [1..100]
forM_ out $ flip enqueueJob queue
replicateM_ 100 $ workStep queue
result <- reverse <$> readIORef ref
result `shouldBe` out
it "failed jobs result in reenqueue" $
do be <- basicChanBackend
crashed <- newIORef False
out <- newIORef False
let worker :: QueueWorker ()
worker =
newQueueWorker $ \() ->
do x <- readIORef crashed
writeIORef crashed True
when x $ writeIORef out True
pure (if x then JOk else JRetry)
let queue = newQueue be worker
enqueueJob () queue `shouldReturn` True
workStep queue
workStep queue
result <- readIORef out
result `shouldBe` True
it "unhandled crashes result in reenqueue" $
do be <- basicChanBackend
crashed <- newIORef False
out <- newIORef False
let worker :: QueueWorker ()
worker =
newQueueWorker $ \() ->
do x <- readIORef crashed
writeIORef crashed True
unless x $ fail "OOOOPS"
writeIORef out True
pure JOk
let queue = newQueue be worker
enqueueJob () queue `shouldReturn` True
workStep queue
workStep queue
result <- readIORef out
result `shouldBe` True