cached-io-1.3.1.0: test/test-cachedIO.hs
module Main (main) where
import Control.Concurrent (forkIO, threadDelay)
import Control.Concurrent.CachedIO (Cached (..), cachedIO)
import Data.Functor (void)
import Data.IORef (IORef, atomicModifyIORef', newIORef, readIORef)
import Test.Tasty (TestTree, defaultMain, testGroup)
import Test.Tasty.HUnit (testCase, (@?=))
tests :: TestTree
tests =
testGroup
"Control.Concurrent.CachedIO"
[ testCase "Action is not called until first use" $ do
ref <- newIORef 0
Cached {} <- cachedIO (5 * 60) $ increment ref
count <- readIORef ref
count @?= 0,
testCase "Action is not called if value is fresh" $ do
ref <- newIORef 0
Cached action <- cachedIO (5 * 60) $ increment ref
void action
void action
count <- readIORef ref
count @?= 1,
testCase "Action is not called if cache is initializing" $ do
ref <- newIORef 0
Cached action <- cachedIO (5 * 60) $ incrementSlow ref
void . forkIO $ void action
void action
count <- readIORef ref
count @?= 1
]
increment :: IORef Int -> IO Int
increment ref = atomicModifyIORef' ref (\i -> (succ i, i))
incrementSlow :: IORef Int -> IO Int
incrementSlow ref = do
res <- atomicModifyIORef' ref (\i -> (succ i, i))
-- waiting AFTER the increase will show that Initialized is necessary
-- because forking a thread takes enough time that the "action >> readIORef"
-- are done before the forked thread has the chance to increment the counter.
threadDelay $ 100 * 1000 -- 100 ms
pure res
main :: IO ()
main = defaultMain tests