packages feed

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