packages feed

kazura-queue-0.1.0.3: test/WVarSpec.hs

{-# LANGUAGE ScopedTypeVariables #-}

module WVarSpec where

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

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

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

takeWVarSpec :: HS.Spec
takeWVarSpec = HS.describe "takeWVar" $ do
    T.whenWVarIsFresh $ \ prepare -> do
        HS.it "takes the value without blocking" $ do
            (val, wv) <- prepare
            r <- WV.takeWVar wv `T.shouldNotBlock` 500000
            r `T.shouldBe` val
    T.whenWVarIsUpdating $ \ prepare -> do
        HS.it "blocks until WVar becomes fresh" $ do
            (val1 :: Int, wv) <- prepare
            val2 <- Q.generate $ Q.arbitrary `Q.suchThat` (/= val1)
            wait <- WV.takeWVar wv `T.shouldBlock` 500000
            M.void $ WV.putWVar wv val2 `T.shouldNotBlock` 500000
            r <- wait `T.shouldAwakeFinish` 500000
            r `T.shouldBe` val2

tryTakeWVarSpec :: HS.Spec
tryTakeWVarSpec = HS.describe "tryTakeWVar" $ do
    T.whenWVarIsFresh $ \ prepare -> do
        HS.it "takes the value without blocking" $ do
            (val :: Int, wv) <- prepare
            r <- WV.tryTakeWVar wv `T.shouldNotBlock` 500000
            r `T.shouldBe` (True, val)
    T.whenWVarIsUpdating $ \ prepare -> do
        HS.it "takes the latest value without blocking" $ do
            (val :: Int, wv) <- prepare
            r <- WV.tryTakeWVar wv `T.shouldNotBlock` 500000
            r `T.shouldBe` (False, val)

putWVarSpec :: HS.Spec
putWVarSpec = do
    T.whenWVarIsFresh $ \ prepare -> do
        HS.it "writes the value without blocking" $ do
            (val1 :: Int, wv) <- prepare
            val2 <- Q.generate $ Q.arbitrary `Q.suchThat` (/= val1)
            M.void $ WV.putWVar wv val2 `T.shouldNotBlock` 500000
            r <- WV.takeWVar wv `T.shouldNotBlock` 500000
            r `T.shouldBe` val2
    T.whenWVarIsUpdating $ \ prepare -> do
        HS.it "writes the value without blocking" $ do
            (val1 :: Int, wv) <- prepare
            val2 <- Q.generate $ Q.arbitrary `Q.suchThat` (/= val1)
            M.void $ WV.putWVar wv val2 `T.shouldNotBlock` 500000
            r <- WV.takeWVar wv `T.shouldNotBlock` 500000
            r `T.shouldBe` val2

readWVarSpec :: HS.Spec
readWVarSpec = HS.describe "readWVar" $ do
    T.whenWVarIsFresh $ \ prepare -> do
        HS.it "reads the value without blocking" $ do
            (val :: Int, wv) <- prepare
            r <- WV.readWVar wv `T.shouldNotBlock` 500000
            r `T.shouldBe` val
    T.whenWVarIsUpdating $ \ prepare -> do
        HS.it "reads the value without blocking" $ do
            (val :: Int, wv) <- prepare
            r <- WV.readWVar wv `T.shouldNotBlock` 500000
            r `T.shouldBe` val

readFreshWVarSpec :: HS.Spec
readFreshWVarSpec = HS.describe "readFreshWVar" $ do
    T.whenWVarIsFresh $ \ prepare -> do
        HS.it "reads the value without blocking" $ do
            (val :: Int, wv) <- prepare
            r <- WV.readFreshWVar wv `T.shouldNotBlock` 500000
            r `T.shouldBe` val
    T.whenWVarIsUpdating $ \ prepare -> do
        HS.it "blocks until WVar becomes fresh" $ do
            (val1 :: Int, wv) <- prepare
            val2 <- Q.generate $ Q.arbitrary `Q.suchThat` (/= val1)
            wait <- WV.readFreshWVar wv `T.shouldBlock` 500000
            M.void $ WV.putWVar wv val2 `T.shouldNotBlock` 500000
            r <- wait `T.shouldAwakeFinish` 500000
            r `T.shouldBe` val2

tryReadFreshWVarSpec :: HS.Spec
tryReadFreshWVarSpec = HS.describe "readFreshWVar" $ do
    T.whenWVarIsFresh $ \ prepare -> do
        HS.it "reads the value without blocking" $ do
            (val :: Int, wv) <- prepare
            r <- WV.tryReadFreshWVar wv `T.shouldNotBlock` 500000
            r `T.shouldBe` (True, val)
    T.whenWVarIsUpdating $ \ prepare -> do
        HS.it "reads the value without blocking" $ do
            (val :: Int, wv) <- prepare
            r <- WV.tryReadFreshWVar wv `T.shouldNotBlock` 500000
            r `T.shouldBe` (False, val)

takeWCachedSpec :: HS.Spec
takeWCachedSpec = HS.describe "takeWCached" $ do
    T.whenWVarIsFresh    $ T.withLatestCache takeValueWithoutBlocking
    T.whenWVarIsUpdating $ T.withLatestCache blocksUntilWVarBecomeFresh
    T.whenWVarIsFreshButCacheStaled    takeValueWithoutBlocking
    T.whenWVarIsUpdatingAndCacheStaled blocksUntilWVarBecomeFresh
    where
        takeValueWithoutBlocking prepare = do
            HS.it "takes the latest value without blocking" $ do
                (_, val :: Int, _, wc) <- prepare
                wt <- WV.takeWCached wc `T.shouldNotBlock` 500000
                WV.readWTicket wt `T.shouldBe` val
        blocksUntilWVarBecomeFresh prepare = do
            HS.it "blocks until WVar becomes fresh" $ do
                (_, val1 :: Int, wv, wc) <- prepare
                val2 <- Q.generate $ Q.arbitrary `Q.suchThat` (/= val1)
                wait <- WV.takeWCached wc `T.shouldBlock` 500000
                M.void $ WV.putWVar wv val2 `T.shouldNotBlock` 500000
                wt <- wait `T.shouldAwakeFinish` 500000
                WV.readWTicket wt `T.shouldBe` val2

tryTakeWCachedSpec :: HS.Spec
tryTakeWCachedSpec = HS.describe "tryTakeWCached" $ do
    T.whenWVarIsFresh $ \ prepare -> do
        HS.it "takes the value without blocking" $ do
            (val :: Int, wv) <- prepare
            wc <- WV.cacheWVar wv
            (ret, wt) <- WV.tryTakeWCached wc `T.shouldNotBlock` 500000
            ret `T.shouldBe` True
            WV.readWTicket wt `T.shouldBe` val
    T.whenWVarIsUpdating $ T.withLatestCache failToTakeButReadLatest
    T.whenWVarIsFreshButCacheStaled    failToTakeButReadLatest
    T.whenWVarIsUpdatingAndCacheStaled failToTakeButReadLatest
  where
    failToTakeButReadLatest prepare = do
        HS.it "fails but reads the latest value without blocking" $ do
            (_, val :: Int, _, wc) <- prepare
            (ret, wt) <- WV.tryTakeWCached wc `T.shouldNotBlock` 500000
            ret `T.shouldBe` False
            WV.readWTicket wt `T.shouldBe` val

putWCachedSpec :: HS.Spec
putWCachedSpec = HS.describe "putWCached" $ do
    T.whenWVarIsFresh    $ T.withLatestCache writeValueWithoutBlocking
    T.whenWVarIsUpdating $ T.withLatestCache writeValueWithoutBlocking
    T.whenWVarIsFreshButCacheStaled    writeValueWithoutBlocking
    T.whenWVarIsUpdatingAndCacheStaled writeValueWithoutBlocking
  where
    writeValueWithoutBlocking prepare = do
        HS.it "writes the value without blocking" $ do
            (_, val1 :: Int, _, wc) <- prepare
            val2 <- Q.generate $ Q.arbitrary `Q.suchThat` (/= val1)
            wt <- WV.putWCached wc val2 `T.shouldNotBlock` 500000
            WV.readWTicket wt `T.shouldBe` val2

tryPutWCachedSpec :: HS.Spec
tryPutWCachedSpec = HS.describe "tryPutWCached" $ do
    T.whenWVarIsFresh    $ T.withLatestCache writeValueWithoutBlocking
    T.whenWVarIsUpdating $ T.withLatestCache writeValueWithoutBlocking
    T.whenWVarIsFreshButCacheStaled    failToWrite
    T.whenWVarIsUpdatingAndCacheStaled failToWrite
  where
    writeValueWithoutBlocking prepare = do
        HS.it "writes the value without blocking" $ do
            (_, val1 :: Int, _, wc) <- prepare
            val2 <- Q.generate $ Q.arbitrary `Q.suchThat` (/= val1)
            (ret, wt) <- WV.tryPutWCached wc val2 `T.shouldNotBlock` 500000
            ret `T.shouldBe` True
            WV.readWTicket wt `T.shouldBe` val2
    failToWrite prepare = do
        HS.it "fails to write the value without blocking" $ do
            (_, val1 :: Int, _, wc) <- prepare
            val2 <- Q.generate $ Q.arbitrary `Q.suchThat` (/= val1)
            (ret, wt) <- WV.tryPutWCached wc val2 `T.shouldNotBlock` 500000
            ret `T.shouldBe` False
            WV.readWTicket wt `T.shouldBe` val1

readWCachedSpec :: HS.Spec
readWCachedSpec = HS.describe "readWCached" $ do
    T.whenWVarIsFresh    $ T.withLatestCache readLatestValue
    T.whenWVarIsUpdating $ T.withLatestCache readLatestValue
    T.whenWVarIsFreshButCacheStaled    readOldValue
    T.whenWVarIsUpdatingAndCacheStaled readOldValue
  where
    readLatestValue prepare = do
        HS.it "reads the latest value in the ticket" $ do
            (_, val :: Int, _, wc) <- prepare
            WV.readWCached wc `T.shouldBe` val
    readOldValue prepare = do
        HS.it "reads the old value in the ticket" $ do
            (val :: Int, _, _, wc) <- prepare
            WV.readWCached wc `T.shouldBe` val

readFreshWCachedSpec :: HS.Spec
readFreshWCachedSpec = HS.describe "readFreshWCached" $ do
    T.whenWVarIsFresh $ \ prepare -> do
        HS.it "reads the latest value without blocking" $ do
            (val :: Int, wv) <- prepare
            wc <- WV.cacheWVar wv
            wt <- WV.readFreshWCached wc `T.shouldNotBlock` 500000
            WV.readWTicket wt `T.shouldBe` val
    T.whenWVarIsUpdating $ \ prepare -> do
        HS.it "blocks until WVar becomes fresh" $ do
            (val1 :: Int, wv) <- prepare
            val2 <- Q.generate $ Q.arbitrary `Q.suchThat` (/= val1)
            wc <- WV.cacheWVar wv
            wait <- WV.readFreshWCached wc `T.shouldBlock` 500000
            M.void $ WV.putWVar wv val2 `T.shouldNotBlock` 500000
            wt <- wait `T.shouldAwakeFinish` 500000
            WV.readWTicket wt `T.shouldBe` val2
    T.whenWVarIsFreshButCacheStaled    readOldValue
    T.whenWVarIsUpdatingAndCacheStaled readOldValue
  where
    readOldValue prepare = do
        HS.it "reads the old value without blocking" $ do
            (val :: Int, _, _, wc) <- prepare
            wt <- WV.readFreshWCached wc `T.shouldNotBlock` 500000
            WV.readWTicket wt `T.shouldBe` val

tryReadFreshWCachedSpec :: HS.Spec
tryReadFreshWCachedSpec = HS.describe "tryReadFreshWCached" $ do
    T.whenWVarIsFresh $ \ prepare -> do
        HS.it "reads the value without blocking" $ do
            (val :: Int, wv) <- prepare
            wc <- WV.cacheWVar wv
            (ret, wt) <- WV.tryReadFreshWCached wc `T.shouldNotBlock` 500000
            ret `T.shouldBe` True
            WV.readWTicket wt `T.shouldBe` val
    T.whenWVarIsUpdating $ \ prepare -> do
        HS.it "reads the value without blocking" $ do
            (val :: Int, wv) <- prepare
            wc <- WV.cacheWVar wv
            (ret, wt) <- WV.tryReadFreshWCached wc `T.shouldNotBlock` 500000
            ret `T.shouldBe` False
            WV.readWTicket wt `T.shouldBe` val
    T.whenWVarIsFreshButCacheStaled $ \ prepare -> do
        HS.it "reads the old value without blocking" $ do
            (val :: Int, _, _, wc) <- prepare
            (ret, wt) <- WV.tryReadFreshWCached wc `T.shouldNotBlock` 500000
            ret `T.shouldBe` True
            WV.readWTicket wt `T.shouldBe` val
    T.whenWVarIsUpdatingAndCacheStaled $ \ prepare -> do
        HS.it "reads the old value without blocking" $ do
            (val :: Int, _, _, wc) <- prepare
            (ret, wt) <- WV.tryReadFreshWCached wc `T.shouldNotBlock` 500000
            ret `T.shouldBe` True
            WV.readWTicket wt `T.shouldBe` val

wvarSpec :: HS.Spec
wvarSpec = do
    takeWVarSpec
    tryTakeWVarSpec
    putWVarSpec
    readWVarSpec
    readFreshWVarSpec
    tryReadFreshWVarSpec

wcachedSpec :: HS.Spec
wcachedSpec = do
    takeWCachedSpec
    tryTakeWCachedSpec
    putWCachedSpec
    tryPutWCachedSpec
    readWCachedSpec
    readFreshWCachedSpec
    tryReadFreshWCachedSpec

spec :: HS.Spec
spec = HS.describe "WVar basic specs" $ do
    HS.describe "WVar"    wvarSpec
    HS.describe "WCached" wcachedSpec