{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DerivingVia #-} {-# LANGUAGE ScopedTypeVariables #-} {-# OPTIONS_GHC -fconstraint-solver-iterations=10 #-} module FreeAsync where -- base import Control.Concurrent (newMVar, takeMVar) import Control.Monad (replicateM_) import Control.Monad.IO.Class import Data.IORef (newIORef, readIORef, writeIORef) import Data.List.NonEmpty (fromList) import GHC.Conc (threadDelay) import GHC.Generics (Generic) -- time import Data.Time (diffUTCTime, getCurrentTime) -- test-framework import Test.Framework (testGroup) -- test-framework-hunit import Test.Framework.Providers.HUnit (testCase) -- test-framework-QuickCheck2 import Test.Framework.Providers.QuickCheck2 (testProperty) -- QuickCheck2 import Test.QuickCheck (Arbitrary, genericShrink, ioProperty, (.&&.), (===)) import Test.QuickCheck.Arbitrary.Generic (Arbitrary (..), genericArbitrary) -- hunit import Test.HUnit ((@?=)) import Test.HUnit.Base (assert) -- monad-schedule import Control.Monad.Schedule.Class (async, scheduleAndFinish) import Control.Monad.Schedule.FreeAsync (FreeAsync, asyncMVar, concurrently, freeAsync, runConcurrently, runFreeAsync) tests = testGroup "FreeAsync" [ testCase "Can lift and run an IORef action" $ do ref <- newIORef "" runFreeAsync $ liftIO $ writeIORef ref "Hello" result <- readIORef ref result @?= "Hello" , testProperty "Can schedule two values" $ \(v1 :: Value) (v2 :: Value) -> ioProperty $ do (result1, result2) <- runFreeAsync $ async (interpretValue 23 v1) (interpretValue 42 v2) return $ result1 === 23 .&&. result2 === 42 , testCase "Can schedule two MVars" $ do var1 <- newMVar 23 var2 <- newMVar 42 result <- runFreeAsync $ async (asyncMVar var1) (asyncMVar var2) result @?= (23, 42) , testCase "Can schedule lifted retrieval of two MVars" $ do var1 <- newMVar 23 var2 <- newMVar 42 result <- runFreeAsync $ async (liftIO $ takeMVar var1) (liftIO $ takeMVar var2) result @?= (23, 42) , testCase "Sequencing adds times" $ do before <- getCurrentTime runFreeAsync $ replicateM_ 100 $ freeAsync $ threadDelay 1000 after <- getCurrentTime let diff = after `diffUTCTime` before print diff assert $ diff >= 0.1 && diff < 0.3 , testCase "Scheduling does things in parallel" $ do before <- getCurrentTime runFreeAsync $ scheduleAndFinish $ fromList $ replicate 100 $ freeAsync $ threadDelay 1000 after <- getCurrentTime let diff = after `diffUTCTime` before print diff assert $ diff > 0.001 && diff <= 0.01 -- Assume overhead is at most 10x at ms durations , testGroup "ConcurrentlyT" [ testCase "Sequencing does things in parallel" $ do before <- getCurrentTime runConcurrently $ replicateM_ 100 $ concurrently $ threadDelay 1000 after <- getCurrentTime let diff = after `diffUTCTime` before print diff assert $ diff > 0.001 && diff <= 0.01 -- Assume overhead is at most 10x at ms durations ] ] data Value = Pure | Lifted | FreeAsync_ | AsyncMVar | TakeMVar | TakeMVarSingleLift deriving (Show, Generic) instance Arbitrary Value where arbitrary = genericArbitrary shrink = genericShrink interpretValue :: a -> Value -> FreeAsync a interpretValue a Pure = pure a interpretValue a Lifted = liftIO (pure a) interpretValue a FreeAsync_ = freeAsync $ pure a interpretValue a AsyncMVar = liftIO (newMVar a) >>= asyncMVar interpretValue a TakeMVar = liftIO (newMVar a) >>= liftIO . takeMVar interpretValue a TakeMVarSingleLift = liftIO (newMVar a >>= takeMVar)