kazura-queue-0.1.0.0: test/KazuraQueueSpec.hs
{-# LANGUAGE ScopedTypeVariables #-}
module KazuraQueueSpec where
import qualified Test.Expectations as T
import qualified Test.KazuraQueue as T
import qualified Test.Hspec as HS
import qualified Test.QuickCheck as Q
import qualified Control.Concurrent.KazuraQueue as KQ
import qualified Control.Monad as M
import qualified Data.IORef as Ref
import qualified System.Mem.Weak as Weak
writeQueueSpec :: HS.Spec
writeQueueSpec = HS.describe "writeQueue" $ do
T.whenQueueIsEmpty $ \ prepare -> do
HS.it "write the value without blocking" . prepare $ \ q -> do
v :: Int <- Q.generate Q.arbitrary
KQ.lengthQueue q `T.shouldReturn` 0
KQ.writeQueue q v `T.shouldNotBlock` 500000
q `T.queueLengthShouldBeIn` (0, 1)
T.whenItemsInQueue (1,10) $ \ prepare -> do
HS.it "write the value without blocking" . prepare $ \ (q, pre) -> do
let len0 = length pre
KQ.lengthQueue q `T.shouldReturn` len0
v :: Int <- Q.generate Q.arbitrary
KQ.writeQueue q v `T.shouldNotBlock` 500000
q `T.queueLengthShouldBeIn` (len0, len0 + 1)
readQueueSpec :: HS.Spec
readQueueSpec = HS.describe "readQueue" $ do
T.whenQueueIsEmpty $ \ prepare -> do
HS.it "blocks until some one writes item" . prepare $ \ q -> do
wait <- KQ.readQueue q `T.shouldBlock` 500000
q `T.queueLengthShouldBeIn` (-1, 0)
val :: Int <- Q.generate Q.arbitrary
KQ.writeQueue q val `T.shouldNotBlock` 500000
r <- wait `T.shouldAwakeFinish` 500000
r `T.shouldBe` val
q `T.queueLengthShouldBeIn` (-1, 0)
HS.it "block and awake out of order (values are in order)" . prepare $ \ q -> do
waits0 <- M.replicateM 2 $ KQ.readQueue q `T.shouldBlock` 500000
q `T.queueLengthShouldBeIn` (-2, 0)
(val1 :: Int, val2) <- Q.generate Q.arbitrary
KQ.writeQueue q val1 `T.shouldNotBlock` 500000
(r1, waits1) <- waits0 `T.onlyOneShouldAwakeFinish` 500000
q `T.queueLengthShouldBeIn` (-2, 0)
KQ.writeQueue q val2 `T.shouldNotBlock` 500000
(r2, _) <- waits1 `T.onlyOneShouldAwakeFinish` 500000
q `T.queueLengthShouldBeIn` (-2, 0)
(r1, r2) `T.shouldBe` (val1, val2)
T.whenQueueIsEmpty $ \ prepare -> do
HS.it "the item in a Queue is not evaluated by write/read" . prepare $ \ q -> do
KQ.writeQueue q ([1..] :: [Int]) `T.shouldNotBlock` 500000
M.void $ KQ.readQueue q `T.shouldNotBlock` 500000
T.whenQueueIsEmpty $ \ prepare -> do
HS.it "the item in a Queue can be garbage collected after read" . prepare $ \ q -> do
ref <- Ref.newIORef True
weak <- Weak.mkWeakPtr ref Nothing
KQ.writeQueue q ref `T.shouldNotBlock` 500000
T.shouldNotBeGarbageCollected weak
M.void $ KQ.readQueue q `T.shouldNotBlock` 500000
T.shouldBeGarbageCollected weak
T.whenItemsInQueue (1,10) $ \ prepare -> do
HS.it "read one value without blocking" . prepare $ \ (q, pre) -> do
r :: Int <- KQ.readQueue q `T.shouldNotBlock` 500000
r `T.shouldBe` head pre
tryReadQueueSpec :: HS.Spec
tryReadQueueSpec = HS.describe "tryReadQueue" $ do
T.whenQueueIsEmpty $ \ prepare -> do
HS.it "immediately returns without reading value" . prepare $ \ q -> do
mret1 <- KQ.tryReadQueue q `T.shouldNotBlock` 500000
mret1 `T.shouldBe` Nothing
q `T.queueLengthShouldBeIn` (-1, 0)
wait <- KQ.readQueue q `T.shouldBlock` 500000
q `T.queueLengthShouldBeIn` (-2, 0)
mret2 <- KQ.tryReadQueue q `T.shouldNotBlock` 500000
mret2 `T.shouldBe` Nothing
q `T.queueLengthShouldBeIn` (-3, 0)
val :: Int <- Q.generate Q.arbitrary
KQ.writeQueue q val `T.shouldNotBlock` 500000
r <- wait `T.shouldAwakeFinish` 500000
r `T.shouldBe` val
q `T.queueLengthShouldBeIn` (-3, 0)
mret3 <- KQ.tryReadQueue q `T.shouldNotBlock` 500000
mret3 `T.shouldBe` Nothing
q `T.queueLengthShouldBeIn` (-4, 0)
HS.it "read value after writing" . prepare $ \ q -> do
(val1 :: Int, val2) <- Q.generate Q.arbitrary
KQ.writeQueue q val1 `T.shouldNotBlock` 500000
mret1 <- KQ.tryReadQueue q `T.shouldNotBlock` 500000
mret1 `T.shouldBe` Just val1
KQ.writeQueue q val2 `T.shouldNotBlock` 500000
mret2 <- KQ.tryReadQueue q `T.shouldNotBlock` 500000
mret2 `T.shouldBe` Just val2
T.whenItemsInQueue (1,10) $ \ prepare -> do
HS.it "read one value without blocking" . prepare $ \ (q, pre) -> do
r :: Maybe Int <- KQ.tryReadQueue q `T.shouldNotBlock` 500000
r `T.shouldBe` Just (head pre)
spec :: HS.Spec
spec = HS.describe "KazuraQueue basic specs" $ do
writeQueueSpec
readQueueSpec
tryReadQueueSpec