content: {-# LANGUAGE DeriveDataTypeable #-}
  module UnliftIO.MemoizeSpec (spec) where
  
  import Control.Concurrent (threadDelay)
  import Control.Monad (replicateM_)
  import Test.Hspec
  import Test.Hspec.QuickCheck
  import UnliftIO
  import Data.Typeable
  
  data Dummy = Dummy
    deriving (Show, Typeable)
  instance Exception Dummy
  
  spec :: Spec
  spec = do
    let basics maker = do
          prop "sanity" $ \i -> do
            x <- maker $ return (i :: Int)
            runMemoized x `shouldReturn` i
          prop "runs once" $ \i -> do
            count <- newIORef (0 :: Int)
            x <- maker $ do
              modifyIORef' count (+ 1)
              return (i :: Int)
            replicateM_ 10 $ runMemoized x `shouldReturn` i
            readIORef count `shouldReturn` 1
          it "runs once with exception" $ do
            count <- newIORef (0 :: Int)
            x <- maker $ do
              modifyIORef' count (+ 1)
              throwIO Dummy
            replicateM_ 10 $ runMemoized x `shouldThrow` (\Dummy -> True)
            readIORef count `shouldReturn` 1
    describe "memoizeRef" $ basics memoizeRef
    describe "memoizeMVar" $ do
      basics memoizeMVar
      prop "runs once in multiple threads" $ \i -> do
        count <- newIORef (0 :: Int)
        x <- memoizeMVar $ do
          threadDelay 10000
          atomicModifyIORef' count $ \cnt -> (cnt + 1, ())
          return (i :: Int)
        replicateConcurrently_ 10 $ runMemoized x `shouldReturn` i
        readIORef count `shouldReturn` 1
  