packages feed

kazura-queue-0.1.0.0: test/WVarConcurrentSpec.hs

{-# LANGUAGE ScopedTypeVariables #-}

module WVarConcurrentSpec where

import qualified Test.Concurrent   as T
import qualified Test.Expectations as T
import qualified Test.Util         as T
import qualified Test.WVar         as T

import qualified Test.Hspec            as HS
import qualified Test.Hspec.QuickCheck as HS

import qualified Control.Concurrent.WVar as WV
import qualified Control.Monad           as M

import qualified Data.List as L

takeWVarSeqSpec :: HS.Spec
takeWVarSeqSpec = HS.describe "takeWVar" $ do
    T.whenWVarIsFresh $ \ prepare -> do
        HS.prop "takes the value before or after putWVar" $ do
            (val1, wv) <- prepare
            [val2] <- T.genSatisfy 1 (/= val1)
            M.void $ WV.putWVar wv val2 `T.concurrently` do
                ret <- WV.takeWVar wv `T.shouldNotBlock` 500000
                T.oneOf
                    [ ret `T.shouldBe` val1
                    , ret `T.shouldBe` val2
                    ]
        HS.prop "take different value" $ do
            (val1, wv) <- prepare
            [val2, val3, val4] <- T.genSatisfy 3 (/= val1)
            ret <- T.mapConcurrently
                [ WV.takeWVar wv <* WV.putWVar wv val2
                , WV.takeWVar wv <* WV.putWVar wv val3
                , WV.takeWVar wv <* WV.putWVar wv val4
                ]
            T.oneOf
                [ ret `T.shouldBe` [val1,val2,val3]
                , ret `T.shouldBe` [val1,val4,val2]
                , ret `T.shouldBe` [val3,val1,val2]
                , ret `T.shouldBe` [val4,val1,val3]
                , ret `T.shouldBe` [val3,val4,val1]
                , ret `T.shouldBe` [val4,val2,val1]
                ]
    T.whenWVarIsUpdating $ \ prepare -> do
        HS.prop "takes the value after putWVar" $ do
            (val1, wv) <- prepare
            [val2] <- T.genSatisfy 1 (/= val1)
            M.void $ WV.putWVar wv val2 `T.concurrently` do
                ret <- WV.takeWVar wv
                ret `T.shouldBe` val2
        HS.prop "take different value" $ do
            (val1 :: Int, wv) <- prepare
            [val2, val3, val4, val5] <- T.genSatisfy 4 (/= val1)
            ret <- T.mapConcurrently
                [ WV.putWVar wv val2 >> return val2
                , WV.takeWVar wv <* WV.putWVar wv val3
                , WV.takeWVar wv <* WV.putWVar wv val4
                , WV.takeWVar wv <* WV.putWVar wv val5
                ]
            T.oneOf
                [ ret `T.shouldBe` [val2,val2,val3,val4]
                , ret `T.shouldBe` [val2,val2,val5,val3]
                , ret `T.shouldBe` [val2,val4,val2,val3]
                , ret `T.shouldBe` [val2,val5,val2,val4]
                , ret `T.shouldBe` [val2,val4,val5,val2]
                , ret `T.shouldBe` [val2,val5,val3,val2]
                ]

tryTakeWVarSeqSpec :: HS.Spec
tryTakeWVarSeqSpec = HS.describe "tryTakeWVar" $ do
    T.whenWVarIsFresh $ \ prepare -> do
        HS.prop "takes the value before or after putWVar" $ do
            (val1 :: Int, wv) <- prepare
            [val2] <- T.genSatisfy 1 (/= val1)
            M.void $ WV.putWVar wv val2 `T.concurrently` do
                ret <- WV.tryTakeWVar wv `T.shouldNotBlock` 500000
                T.oneOf
                    [ ret `T.shouldBe` (True, val1)
                    , ret `T.shouldBe` (True, val2)
                    ]
        HS.prop "all read same value but only one succeeded" $ do
            (val :: Int, wv) <- prepare
            ret <- T.mapConcurrently
                [ WV.tryTakeWVar wv
                , WV.tryTakeWVar wv
                , WV.tryTakeWVar wv
                ]
            T.oneOf
                [ ret `T.shouldBe` [(True,val),(False,val),(False,val)]
                , ret `T.shouldBe` [(False,val),(True,val),(False,val)]
                , ret `T.shouldBe` [(False,val),(False,val),(True,val)]
                ]
    T.whenWVarIsUpdating $ \ prepare -> do
        HS.prop "takes the value before(failure) or after(success) putWVar" $ do
            (val1 :: Int, wv) <- prepare
            [val2] <- T.genSatisfy 1 (/= val1)
            M.void $ WV.putWVar wv val2 `T.concurrently` do
                ret <- WV.tryTakeWVar wv `T.shouldNotBlock` 500000
                T.oneOf
                    [ ret `T.shouldBe` (False, val1)
                    , ret `T.shouldBe` (True,  val2)
                    ]
        HS.prop "all read same value and fail" $ do
            (val :: Int, wv) <- prepare
            ret <- T.mapConcurrently
                [ WV.tryTakeWVar wv
                , WV.tryTakeWVar wv
                , WV.tryTakeWVar wv
                ]
            ret `T.shouldBe` [(False,val),(False,val),(False,val)]

readFreshWVarSeqSpec :: HS.Spec
readFreshWVarSeqSpec = HS.describe "readFreshWVar" $ do
    T.whenWVarIsFresh $ \ prepare -> do
        HS.prop "reads the value before or after putWVar" $ do
            (val1 :: Int, wv) <- prepare
            [val2] <- T.genSatisfy 1 (/= val1)
            M.void $ WV.putWVar wv val2 `T.concurrently` do
                ret <- WV.readFreshWVar wv `T.shouldNotBlock` 500000
                T.oneOf
                    [ ret `T.shouldBe` val1
                    , ret `T.shouldBe` val2
                    ]
        HS.prop "read same value" $ do
            (val :: Int, wv) <- prepare
            ret <- T.mapConcurrently
                [ WV.readFreshWVar wv
                , WV.readFreshWVar wv
                , WV.readFreshWVar wv
                ]
            ret `T.shouldBe` [val,val,val]
    T.whenWVarIsUpdating $ \ prepare -> do
        HS.prop "reads the value after putWVar" $ do
            (val1 :: Int, wv) <- prepare
            [val2] <- T.genSatisfy 1 (/= val1)
            M.void $ WV.putWVar wv val2 `T.concurrently` do
                ret <- WV.readFreshWVar wv
                ret `T.shouldBe` val2
        HS.prop "read same value" $ do
            (val1 :: Int, wv) <- prepare
            [val2] <- T.genSatisfy 1 (/= val1)
            ret <- T.mapConcurrently
                [ WV.putWVar wv val2 >> return val2
                , WV.readFreshWVar wv
                , WV.readFreshWVar wv
                , WV.readFreshWVar wv
                ]
            ret `T.shouldBe` [val2,val2,val2,val2]

tryReadFreshWVarSeqSpec :: HS.Spec
tryReadFreshWVarSeqSpec = HS.describe "tryReadWVar" $ do
    T.whenWVarIsFresh $ \ prepare -> do
        HS.prop "reads the old value" $ do
            (val1 :: Int, wv) <- prepare
            [val2] <- T.genSatisfy 1 (/= val1)
            M.void $ WV.putWVar wv val2 `T.concurrently` do
                ret <- WV.tryReadFreshWVar wv `T.shouldNotBlock` 500000
                T.oneOf
                    [ ret `T.shouldBe` (True, val1)
                    , ret `T.shouldBe` (True, val2)
                    ]
        HS.prop "all read same value and succeed" $ do
            (val :: Int, wv) <- prepare
            ret <- T.mapConcurrently
                [ WV.tryReadFreshWVar wv
                , WV.tryReadFreshWVar wv
                , WV.tryReadFreshWVar wv
                ]
            ret `T.shouldBe` [(True,val),(True,val),(True,val)]
    T.whenWVarIsUpdating $ \ prepare -> do
        HS.prop "reads the value before(failure) or after(success) putWVar" $ do
            (val1 :: Int, wv) <- prepare
            [val2] <- T.genSatisfy 1 (/= val1)
            M.void $ WV.putWVar wv val2 `T.concurrently` do
                ret <- WV.tryReadFreshWVar wv `T.shouldNotBlock` 500000
                T.oneOf
                    [ ret `T.shouldBe` (False, val1)
                    , ret `T.shouldBe` (True,  val2)
                    ]
        HS.prop "all read same value and fail" $ do
            (val :: Int, wv) <- prepare
            ret <- T.mapConcurrently
                [ WV.tryReadFreshWVar wv
                , WV.tryReadFreshWVar wv
                , WV.tryReadFreshWVar wv
                ]
            ret `T.shouldBe` [(False,val),(False,val),(False,val)]

takeWCachedSeqSpec :: HS.Spec
takeWCachedSeqSpec = HS.describe "takeWCached" $ do
    T.whenWVarIsFresh . T.withLatestCache $ \ prepare -> do
        HS.prop "takes the value before or after putWCached" $ do
            (_, val1 :: Int, _, wc) <- prepare
            [val2] <- T.genSatisfy 1 (/= val1)
            M.void $ WV.putWCached wc val2 `T.concurrently` do
                wt <- WV.takeWCached wc `T.shouldNotBlock` 500000
                T.oneOf
                    [ WV.readWTicket wt `T.shouldBe` val1
                    , WV.readWTicket wt `T.shouldBe` val2
                    ]
        HS.prop "take different value" $ do
            (_, val1 :: Int, _, wc) <- prepare
            [val2, val3, val4] <- T.genSatisfy 3 (/= val1)
            ret <- fmap WV.readWTicket <$> T.mapConcurrently
                [ WV.takeWCached wc <* WV.putWCached wc val2
                , WV.takeWCached wc <* WV.putWCached wc val3
                , WV.takeWCached wc <* WV.putWCached wc val4
                ]
            T.oneOf
                [ ret `T.shouldBe` [val1,val2,val3]
                , ret `T.shouldBe` [val1,val4,val2]
                , ret `T.shouldBe` [val3,val1,val2]
                , ret `T.shouldBe` [val4,val1,val3]
                , ret `T.shouldBe` [val3,val4,val1]
                , ret `T.shouldBe` [val4,val2,val1]
                ]
    T.whenWVarIsUpdating . T.withLatestCache $ \ prepare -> do
        HS.prop "takes the value after putWCached" $ do
            (_, val1 :: Int, _, wc) <- prepare
            [val2] <- T.genSatisfy 1 (/= val1)
            M.void $ WV.putWCached wc val2 `T.concurrently` do
                ret <- WV.takeWCached wc
                WV.readWTicket ret `T.shouldBe` val2
        HS.prop "take different value" $ do
            (_, val1 :: Int, _, wc) <- prepare
            [val2, val3, val4, val5] <- T.genSatisfy 4 (/= val1)
            ret <- T.mapConcurrently
                [ WV.putWCached wc val2 >> return val2
                , WV.readWTicket <$> WV.takeWCached wc <* WV.putWCached wc val3
                , WV.readWTicket <$> WV.takeWCached wc <* WV.putWCached wc val4
                , WV.readWTicket <$> WV.takeWCached wc <* WV.putWCached wc val5
                ]
            T.oneOf
                [ ret `T.shouldBe` [val2,val2,val3,val4]
                , ret `T.shouldBe` [val2,val2,val5,val3]
                , ret `T.shouldBe` [val2,val4,val2,val3]
                , ret `T.shouldBe` [val2,val5,val2,val4]
                , ret `T.shouldBe` [val2,val4,val5,val2]
                , ret `T.shouldBe` [val2,val5,val3,val2]
                ]
    T.whenWVarIsFreshButCacheStaled $ \ prepare -> do
        HS.prop "takes the value before or after putWCached" $ do
            (_, val1 :: Int, _, wc) <- prepare
            [val2] <- T.genSatisfy 1 (/= val1)
            M.void $ WV.putWCached wc val2 `T.concurrently` do
                wt <- WV.takeWCached wc `T.shouldNotBlock` 500000
                T.oneOf
                    [ WV.readWTicket wt `T.shouldBe` val1
                    , WV.readWTicket wt `T.shouldBe` val2
                    ]
        HS.prop "take different value" $ do
            (_, val1 :: Int, _, wc) <- prepare
            [val2, val3, val4] <- T.genSatisfy 3 (/= val1)
            ret <- fmap WV.readWTicket <$> T.mapConcurrently
                [ WV.takeWCached wc <* WV.putWCached wc val2
                , WV.takeWCached wc <* WV.putWCached wc val3
                , WV.takeWCached wc <* WV.putWCached wc val4
                ]
            T.oneOf
                [ ret `T.shouldBe` [val1,val2,val3]
                , ret `T.shouldBe` [val1,val4,val2]
                , ret `T.shouldBe` [val3,val1,val2]
                , ret `T.shouldBe` [val4,val1,val3]
                , ret `T.shouldBe` [val3,val4,val1]
                , ret `T.shouldBe` [val4,val2,val1]
                ]
    T.whenWVarIsUpdatingAndCacheStaled $ \ prepare -> do
        HS.prop "takes the value after putWCached" $ do
            (_, val1 :: Int, _, wc) <- prepare
            [val2] <- T.genSatisfy 1 (/= val1)
            M.void $ WV.putWCached wc val2 `T.concurrently` do
                ret <- WV.takeWCached wc
                WV.readWTicket ret `T.shouldBe` val2
        HS.prop "take different value" $ do
            (_, val1 :: Int, _, wc) <- prepare
            [val2, val3, val4, val5] <- T.genSatisfy 4 (/= val1)
            ret <- T.mapConcurrently
                [ WV.putWCached wc val2 >> return val2
                , WV.readWTicket <$> WV.takeWCached wc <* WV.putWCached wc val3
                , WV.readWTicket <$> WV.takeWCached wc <* WV.putWCached wc val4
                , WV.readWTicket <$> WV.takeWCached wc <* WV.putWCached wc val5
                ]
            T.oneOf
                [ ret `T.shouldBe` [val2,val2,val3,val4]
                , ret `T.shouldBe` [val2,val2,val5,val3]
                , ret `T.shouldBe` [val2,val4,val2,val3]
                , ret `T.shouldBe` [val2,val5,val2,val4]
                , ret `T.shouldBe` [val2,val4,val5,val2]
                , ret `T.shouldBe` [val2,val5,val3,val2]
                ]

tryTakeWCachedSeqSpec :: HS.Spec
tryTakeWCachedSeqSpec = HS.describe "tryTakeWCached" $ do
    T.whenWVarIsFresh . T.withLatestCache $ \ prepare -> do
        HS.prop "takes the value before(failure)/after(success) putWCached" $ do
            (_, val1 :: Int, _, wc) <- prepare
            [val2] <- T.genSatisfy 1 (/= val1)
            M.void $ WV.putWCached wc val2 `T.concurrently` do
                (ret, wt) <- WV.tryTakeWCached wc `T.shouldNotBlock` 500000
                T.oneOf
                    [ (ret, WV.readWTicket wt) `T.shouldBe` (True, val1)
                    , (ret, WV.readWTicket wt) `T.shouldBe` (False, val2)
                    ]
        HS.prop "all read same value but only one succeeded" $ do
            (_, val1 :: Int, _, wc) <- prepare
            ret <- fmap (fmap WV.readWTicket) <$> T.mapConcurrently
                [ WV.tryTakeWCached wc
                , WV.tryTakeWCached wc
                , WV.tryTakeWCached wc
                ]
            T.oneOf
                [ ret `T.shouldBe` [(True,val1),(False,val1),(False,val1)]
                , ret `T.shouldBe` [(False,val1),(True,val1),(False,val1)]
                , ret `T.shouldBe` [(False,val1),(False,val1),(True,val1)]
                ]
    T.whenWVarIsUpdating . T.withLatestCache $ \ prepare -> do
        HS.prop "takes the value before or after putWCached with failure" $ do
            (_, val1 :: Int, _, wc) <- prepare
            [val2] <- T.genSatisfy 1 (/= val1)
            M.void $ WV.putWCached wc val2 `T.concurrently` do
                (ret, wt) <- WV.tryTakeWCached wc `T.shouldNotBlock` 500000
                T.oneOf
                    [ (ret, WV.readWTicket wt) `T.shouldBe` (False, val1)
                    , (ret, WV.readWTicket wt) `T.shouldBe` (False, val2)
                    ]
        HS.prop "all read same value and fail" $ do
            (_, val1 :: Int, _, wc) <- prepare
            ret <- fmap (fmap WV.readWTicket) <$> T.mapConcurrently
                [ WV.tryTakeWCached wc
                , WV.tryTakeWCached wc
                , WV.tryTakeWCached wc
                ]
            ret `T.shouldBe` [(False,val1),(False,val1),(False,val1)]
    T.whenWVarIsFreshButCacheStaled $ \ prepare -> do
        HS.prop "takes the value before or after putWCached with failure" $ do
            (_, val1 :: Int, _, wc) <- prepare
            [val2] <- T.genSatisfy 1 (/= val1)
            M.void $ WV.putWCached wc val2 `T.concurrently` do
                (ret, wt) <- WV.tryTakeWCached wc `T.shouldNotBlock` 500000
                T.oneOf
                    [ (ret, WV.readWTicket wt) `T.shouldBe` (False, val1)
                    , (ret, WV.readWTicket wt) `T.shouldBe` (False, val2)
                    ]
        HS.prop "all read same value and fail" $ do
            (_, val1 :: Int, _, wc) <- prepare
            ret <- fmap (fmap WV.readWTicket) <$> T.mapConcurrently
                [ WV.tryTakeWCached wc
                , WV.tryTakeWCached wc
                , WV.tryTakeWCached wc
                ]
            ret `T.shouldBe` [(False,val1),(False,val1),(False,val1)]
    T.whenWVarIsUpdatingAndCacheStaled $ \ prepare -> do
        HS.prop "takes the value before or after putWCached with failure" $ do
            (_, val1 :: Int, _, wc) <- prepare
            [val2] <- T.genSatisfy 1 (/= val1)
            M.void $ WV.putWCached wc val2 `T.concurrently` do
                (ret, wt) <- WV.tryTakeWCached wc `T.shouldNotBlock` 500000
                T.oneOf
                    [ (ret, WV.readWTicket wt) `T.shouldBe` (False, val1)
                    , (ret, WV.readWTicket wt) `T.shouldBe` (False, val2)
                    ]
        HS.prop "all read same value and fail" $ do
            (_, val1 :: Int, _, wc) <- prepare
            ret <- fmap (fmap WV.readWTicket) <$> T.mapConcurrently
                [ WV.tryTakeWCached wc
                , WV.tryTakeWCached wc
                , WV.tryTakeWCached wc
                ]
            ret `T.shouldBe` [(False,val1),(False,val1),(False,val1)]

readFreshWCachedSeqSpec :: HS.Spec
readFreshWCachedSeqSpec = HS.describe "readFreshWCached" $ do
    T.whenWVarIsFresh . T.withLatestCache $ \ prepare -> do
        HS.prop "reads the value before or after putWCached" $ do
            (val1 :: Int, _, _, wc) <- prepare
            [val2] <- T.genSatisfy 1 (/= val1)
            M.void $ WV.putWCached wc val2 `T.concurrently` do
                wt <- WV.readFreshWCached wc `T.shouldNotBlock` 500000
                WV.readWTicket wt `T.shouldBe` val1
        HS.prop "read same value" $ do
            (val1 :: Int, _, _, wc) <- prepare
            ret <- fmap WV.readWTicket <$> T.mapConcurrently
                [ WV.readFreshWCached wc
                , WV.readFreshWCached wc
                , WV.readFreshWCached wc
                ]
            ret `T.shouldBe` [val1,val1,val1]
    T.whenWVarIsUpdating . T.withLatestCache $ \ prepare -> do
        HS.prop "reads the value after putWCached" $ do
            (val1 :: Int, _, _, wc) <- prepare
            [val2] <- T.genSatisfy 1 (/= val1)
            M.void $ WV.putWCached wc val2 `T.concurrently` do
                wt <- WV.readFreshWCached wc
                WV.readWTicket wt `T.shouldBe` val2
        HS.prop "read same value" $ do
            (val1 :: Int, _, _, wc) <- prepare
            [val2] <- T.genSatisfy 1 (/= val1)
            ret <- T.mapConcurrently
                [ WV.putWCached wc val2 >> return val2
                , WV.readWTicket <$> WV.readFreshWCached wc
                , WV.readWTicket <$> WV.readFreshWCached wc
                , WV.readWTicket <$> WV.readFreshWCached wc
                ]
            ret `T.shouldBe` [val2,val2,val2,val2]
    T.whenWVarIsFreshButCacheStaled $ \ prepare -> do
        HS.prop "reads the value before or after putWCached" $ do
            (val1 :: Int, _, _, wc) <- prepare
            [val2] <- T.genSatisfy 1 (/= val1)
            M.void $ WV.putWCached wc val2 `T.concurrently` do
                wt <- WV.readFreshWCached wc `T.shouldNotBlock` 500000
                WV.readWTicket wt `T.shouldBe` val1
        HS.prop "read same value" $ do
            (val1 :: Int, _, _, wc) <- prepare
            ret <- fmap WV.readWTicket <$> T.mapConcurrently
                [ WV.readFreshWCached wc
                , WV.readFreshWCached wc
                , WV.readFreshWCached wc
                ]
            ret `T.shouldBe` [val1,val1,val1]
    T.whenWVarIsUpdatingAndCacheStaled $ \ prepare -> do
        HS.prop "reads the value after putWCached" $ do
            (val1 :: Int, _, _, wc) <- prepare
            [val2] <- T.genSatisfy 1 (/= val1)
            M.void $ WV.putWCached wc val2 `T.concurrently` do
                ret <- WV.readWTicket <$> WV.readFreshWCached wc
                ret `T.shouldBe` val1
        HS.prop "read same value" $ do
            (val1 :: Int, _, _, wc) <- prepare
            [val2] <- T.genSatisfy 1 (/= val1)
            ret <- T.mapConcurrently
                [ WV.putWCached wc val2 >> return val2
                , WV.readWTicket <$> WV.readFreshWCached wc
                , WV.readWTicket <$> WV.readFreshWCached wc
                , WV.readWTicket <$> WV.readFreshWCached wc
                ]
            ret `T.shouldBe` [val2,val1,val1,val1]

tryReadFreshWCachedSeqSpec :: HS.Spec
tryReadFreshWCachedSeqSpec = HS.describe "tryReadWCached" $ do
    T.whenWVarIsFresh . T.withLatestCache $ \ prepare -> do
        HS.prop "reads the old value and succeed" $ do
            (val1 :: Int, _, _, wc) <- prepare
            [val2] <- T.genSatisfy 1 (/= val1)
            M.void $ WV.putWCached wc val2 `T.concurrently` do
                ret <- WV.tryReadFreshWCached wc `T.shouldNotBlock` 500000
                fmap WV.readWTicket ret `T.shouldBe` (True, val1)
        HS.prop "all read same value and succeed" $ do
            (val1 :: Int, _, _, wc) <- prepare
            ret <- fmap (fmap WV.readWTicket) <$> T.mapConcurrently
                [ WV.tryReadFreshWCached wc
                , WV.tryReadFreshWCached wc
                , WV.tryReadFreshWCached wc
                ]
            ret `T.shouldBe` [(True,val1),(True,val1),(True,val1)]
    T.whenWVarIsUpdating . T.withLatestCache $ \ prepare -> do
        HS.prop "reads the old value and fail" $ do
            (val1 :: Int, _, _, wc) <- prepare
            [val2] <- T.genSatisfy 1 (/= val1)
            M.void $ WV.putWCached wc val2 `T.concurrently` do
                ret <- WV.tryReadFreshWCached wc `T.shouldNotBlock` 500000
                fmap WV.readWTicket ret `T.shouldBe` (False, val1)
        HS.prop "all read same value and fail" $ do
            (val1 :: Int, _, _, wc) <- prepare
            ret <- fmap (fmap WV.readWTicket) <$> T.mapConcurrently
                [ WV.tryReadFreshWCached wc
                , WV.tryReadFreshWCached wc
                , WV.tryReadFreshWCached wc
                ]
            ret `T.shouldBe` [(False,val1),(False,val1),(False,val1)]
    T.whenWVarIsFreshButCacheStaled $ \ prepare -> do
        HS.prop "reads the old value and succeed" $ do
            (val1 :: Int, _, _, wc) <- prepare
            [val2] <- T.genSatisfy 1 (/= val1)
            M.void $ WV.putWCached wc val2 `T.concurrently` do
                ret <- WV.tryReadFreshWCached wc `T.shouldNotBlock` 500000
                fmap WV.readWTicket ret `T.shouldBe` (True, val1)
        HS.prop "all read same value and succeed" $ do
            (val1 :: Int, _, _, wc) <- prepare
            ret <- fmap (fmap WV.readWTicket) <$> T.mapConcurrently
                [ WV.tryReadFreshWCached wc
                , WV.tryReadFreshWCached wc
                , WV.tryReadFreshWCached wc
                ]
            ret `T.shouldBe` [(True,val1),(True,val1),(True,val1)]
    T.whenWVarIsUpdating . T.withLatestCache $ \ prepare -> do
        HS.prop "reads the old value and fail" $ do
            (val1 :: Int, _, _, wc) <- prepare
            [val2] <- T.genSatisfy 1 (/= val1)
            M.void $ WV.putWCached wc val2 `T.concurrently` do
                ret <- WV.tryReadFreshWCached wc `T.shouldNotBlock` 500000
                fmap WV.readWTicket ret `T.shouldBe` (False, val1)
        HS.prop "all read same value and fail" $ do
            (val1 :: Int, _, _, wc) <- prepare
            ret <- fmap (fmap WV.readWTicket) <$> T.mapConcurrently
                [ WV.tryReadFreshWCached wc
                , WV.tryReadFreshWCached wc
                , WV.tryReadFreshWCached wc
                ]
            ret `T.shouldBe` [(False,val1),(False,val1),(False,val1)]

tryTakeAndPutCachedSeqSpec :: HS.Spec
tryTakeAndPutCachedSeqSpec =
    HS.prop "tryTakeWCached and putWCached perform atomic modification" $ do
        wv <- WV.newWVar (0 :: Int)
        wc <- WV.cacheWVar wv
        ret <- L.sort . L.concat <$> T.mapConcurrently (countConc10 wc)
        ret `T.shouldBe` [1..1000]
    where
        countConc10 wc = L.replicate 10 $ count100 wc
        count100 wc = M.replicateM 100 $ countOne wc
        countOne wc = do
            (suc, wt1) <- WV.tryTakeWCached wc
            let val = WV.readWTicket wt1
            if suc
                then do
                    wt2 <- WV.putWCached wc $ val + 1
                    return $ WV.readWTicket wt2
                else do
                    wt2 <- WV.readFreshWCached wc { WV.cachedTicket = wt1 }
                    countOne wc { WV.cachedTicket = wt2 }

wvarSpec :: HS.Spec
wvarSpec = do
    takeWVarSeqSpec
    tryTakeWVarSeqSpec
    readFreshWVarSeqSpec
    tryReadFreshWVarSeqSpec

wcachedSpec :: HS.Spec
wcachedSpec = do
    takeWCachedSeqSpec
    tryTakeWCachedSeqSpec
    readFreshWCachedSeqSpec
    tryReadFreshWCachedSeqSpec

spec :: HS.Spec
spec = HS.describe "WVar concurrent specs" $ do
    HS.describe "WVar"    wvarSpec
    HS.describe "WCached" wcachedSpec
    HS.describe "combination" tryTakeAndPutCachedSeqSpec