purescript-sync/test/Test.Main.purs

122 lines
3.9 KiB
Haskell
Raw Normal View History

2023-11-02 01:13:31 +00:00
module Test.Main where
import Prelude
2023-11-06 23:38:03 +00:00
import Control.Monad.Error.Class (throwError, try)
import Control.Monad.Rec.Class (Step(..), forever, tailRecM, untilJust)
import Control.Monad.State.Async (AsyncStateT, asyncModify, asyncPut, asyncRead, asyncWrite, runAsyncState, runMutexState)
2023-11-02 01:13:31 +00:00
import Control.Monad.Trans.Class (lift)
2023-11-06 23:38:03 +00:00
import Control.Parallel (parOneOf, parSequence_, parallel, sequential)
import Data.Array as Array
2023-11-02 01:13:31 +00:00
import Data.Async.Class (class AsyncState)
import Data.Async.Mutex (Mutex)
import Data.Async.RwLock (RwLock)
import Data.Either (isLeft)
import Data.Identity (Identity)
2023-11-06 23:38:03 +00:00
import Data.Maybe (Maybe(..))
2023-11-02 01:13:31 +00:00
import Data.Newtype (wrap)
2023-11-06 23:38:03 +00:00
import Data.Traversable (for_)
import Data.Tuple.Nested ((/\))
2023-11-02 01:13:31 +00:00
import Effect (Effect)
import Effect.Aff (Aff, delay, launchAff_)
2023-11-06 23:38:03 +00:00
import Effect.Aff.Class (liftAff)
import Effect.Class (liftEffect)
import Effect.Exception (error)
import Effect.Random (randomBool)
2023-11-02 01:13:31 +00:00
import Test.Spec (SpecT, describe, it)
import Test.Spec.Assertions (shouldEqual, shouldSatisfy)
import Test.Spec.Reporter (consoleReporter)
import Test.Spec.Runner (runSpec)
common :: forall @w wl rl. AsyncState w wl rl => SpecT Aff Unit Identity Unit
common = do
it "does not throw" $ runAsyncState unit (pure @(AsyncStateT w Unit Aff) unit)
it "supports single-threaded state manipulation" do
runAsyncState 0 do
pure @(AsyncStateT w Int Aff) unit
flip shouldSatisfy isLeft =<< try (asyncRead $ shouldEqual 1)
asyncPut 1
asyncRead $ shouldEqual 1
asyncModify $ \n -> pure $ n + 2
asyncRead $ shouldEqual 3
it "supports concurrent state manipulation" do
let
t1 = parallel do
lift $ delay $ wrap 50.0
asyncModify
( \s -> do
delay $ wrap 100.0
pure $ s <> "john"
)
pure unit
t2 = parallel do
asyncPut "hello, "
lift $ delay $ wrap 60.0
asyncModify (pure <<< (_ <> "!"))
pure unit
runAsyncState "" do
pure @(AsyncStateT w String Aff) unit
sequential (pure (\_ _ -> unit) <*> t1 <*> t2)
asyncRead (shouldEqual "hello, john!")
2023-11-06 23:38:03 +00:00
it "supports concurrent state manipulation 2" do
let
t = do
_ <- asyncRead pure
asyncWrite (pure <<< (unit /\ _) <<< (_ <> "a"))
pure unit
runAsyncState "" do
pure @(AsyncStateT w String Aff) unit
parSequence_ $ Array.replicate 10 t
asyncRead (shouldEqual "aaaaaaaaaa")
it "supports parallel delay in monadrec" do
let
done = liftAff $ delay $ wrap 5000.0
go 0 = pure $ Done unit
go n = do
liftAff $ delay $ wrap 2.0
_ <- asyncRead pure
_ <- asyncModify pure
pure $ Loop (n - 1)
runAsyncState "" do
pure @(AsyncStateT w String Aff) unit
parOneOf
[ done
, tailRecM go 100
, tailRecM go 100
, tailRecM go 100
, tailRecM go 100
]
it "setting the state to a value unblocks MonadRec" do
let
delayThenDone = do
liftAff $ delay $ wrap 100.0
asyncPut true
wait = untilJust do
done <- asyncRead pure
pure $ if done then Just unit else Nothing
runMutexState false $ parSequence_ [ delayThenDone, wait ]
it "throwing with lock does not block other threads" do
let
t = do
readThrows <- liftEffect randomBool
writeThrows <- liftEffect randomBool
void $ try $ asyncRead (const $ if readThrows then throwError $ error "fail" else pure unit)
void $ try $ asyncModify (\s -> if writeThrows then throwError $ error "fail" else pure s)
pure unit
for_ (Array.replicate 100 unit) \_ -> do
runAsyncState "" do
pure @(AsyncStateT w String Aff) unit
parSequence_ $ Array.replicate 100 t
2023-11-02 01:13:31 +00:00
main :: Effect Unit
main = launchAff_ $ runSpec [ consoleReporter ] do
describe "AsyncStateT" do
describe "MutexStateT" do
common @Mutex
describe "RwLockStateT" do
common @RwLock