packages feed

flush-queue-1.0.0: test/Control/Concurrent/BFQueueSpec.hs

module Control.Concurrent.BFQueueSpec (spec) where

import Control.Concurrent.Async
import Control.Concurrent.BFQueue
import Data.List

import Test.Hspec
import Test.QuickCheck
import Test.QuickCheck.Monadic


prop_FillFlushNonBlocking :: [[Int]] -> Property
prop_FillFlushNonBlocking lss = monadicIO $ do
  ls <- run $ do
    q <- newBFQueue (fromIntegral (sum (map length lss)))
    mapConcurrently_ (foldMap (writeBFQueue q)) lss
    flushBFQueue q
  return (sort ls === sort (concat lss))

prop_FillAndBlockFlush :: Positive Int -> [Int] -> Int -> Property
prop_FillAndBlockFlush (Positive bound) ls oneExtra =
  bound < length ls ==> monadicIO $ do
    let (fillWith, leftOver) = splitAt bound ls
    run $ do
      q <- newBFQueue $ fromIntegral bound
      isSuccess' <- and <$> mapConcurrently (tryWriteBFQueue q) fillWith
      hasSpace <- or <$> mapConcurrently (tryWriteBFQueue q) leftOver
      len <- lengthBFQueue q
      eLs <- race (writeBFQueue q oneExtra >> flushBFQueue q) (flushBFQueue q)
      return $
        conjoin
          [ counterexample "Queue wasn't fully filled up" isSuccess'
          , counterexample "Left over was placed on the queue" (not hasSpace)
          , fromIntegral len === length fillWith
          , either
              (\o ->
                 o === [oneExtra] .||.
                 counterexample "Placed an element on the full queue concurrently" False)
              (\ls' -> sort ls' === sort fillWith)
              eLs
          ]

prop_FillReadTakeNonBlocking :: NonEmptyList Int -> Property
prop_FillReadTakeNonBlocking (NonEmpty xs) =
  monadicIO $
  run $ do
    let i = fromIntegral $ length xs - 1
    q <- newBFQueue (i + 1)
    mapM_ (writeBFQueue q) xs
    [x'] <- takeBFQueue 1 q
    xs' <- takeBFQueue i q
    isEmpty <- isEmptyBFQueue q
    return (head xs === x' .&&. tail xs === xs' .&&. counterexample "Queue is non-empty" isEmpty)

spec :: Spec
spec =
  describe "Fill+Flush" $ do
    it "FillFlushNonBlocking" $ property prop_FillFlushNonBlocking
    it "FillAndBlockFlush" $ property prop_FillAndBlockFlush
    it "FillReadTakeNonBlocking" $ property prop_FillReadTakeNonBlocking