packages feed

extensible-effects-concurrent-0.2.0.2: test/ForkIOScheduler.hs

module ForkIOScheduler where

import Control.Exception
import Control.Concurrent
import Control.Concurrent.STM
import Control.Eff
import Control.Eff.Lift
import Control.Eff.Concurrent.Process
import Control.Eff.Concurrent.Process.ForkIOScheduler as Scheduler
import Control.Monad (void, forever)
import Test.Tasty
import Test.Tasty.HUnit
import Data.Dynamic

timeoutSeconds :: Integer -> Timeout
timeoutSeconds seconds = Timeout (seconds * 1000000) (show seconds ++ "s")

test_IOExceptionsIsolated :: TestTree
test_IOExceptionsIsolated =
  localOption
  (timeoutSeconds 30)
    $ testGroup "one process throws an IO exception, the other continues unimpaired"
       [ testCase ("process 2 exits with: "++ howToExit
                    ++ " - while process 1 is busy with: " ++ busyWith)
        $ do aVar <- newEmptyTMVarIO
             Scheduler.defaultMain
               $ do p1 <- spawn $ forever busyEffect
                    lift (threadDelay 1000)
                    void $ spawn $ do lift (threadDelay 1000)
                                      doExit
                    lift (threadDelay 100000)
                    wasStillRunningP1 <- sendShutdown forkIoScheduler p1
                    lift (atomically (putTMVar aVar wasStillRunningP1))

             wasStillRunningP1 <- atomically (takeTMVar aVar)
             assertBool "the other process was still running" wasStillRunningP1

       | (busyWith, busyEffect) <-
         [ ("receiving", void (send (ReceiveMessage @SchedulerIO)))
         , ("sending", void (send (SendMessage @SchedulerIO 44444 (toDyn "test message"))))
         , ("sending shutdown", void (send (SendShutdown @SchedulerIO 44444)))
         , ("selfpid-ing", void (send (SelfPid @SchedulerIO)))
         , ("spawn-ing", void (send (Spawn @SchedulerIO (void (send (ReceiveMessage @SchedulerIO))))))
         ]
       , (howToExit, doExit) <-
         [ ("throw async exception", void (lift (throw UserInterrupt)))
         , ("division by zero", void (return ((123::Int) `div` 0) >>= lift . putStrLn . show ))
         , ("call 'fail'", void (fail "test fail"))
         , ("call 'error'", void (error "test error"))
         ]
       ]

test_mainProcessSpawnsAChildAndReturns :: TestTree
test_mainProcessSpawnsAChildAndReturns =
  localOption
  (timeoutSeconds 30)
  (testCase "spawn a child and return"
   (Scheduler.defaultMain
     (void (spawn (void (receiveMessage forkIoScheduler))))))

test_mainProcessSpawnsAChildAndExitsNormally :: TestTree
test_mainProcessSpawnsAChildAndExitsNormally =
  localOption
  (timeoutSeconds 30)
  (testCase "spawn a child and exit normally"
   (Scheduler.defaultMain
     (do void (spawn (void (receiveMessage forkIoScheduler)))
         void (exitNormally forkIoScheduler)
         fail "This should not happen!!"
     )))


test_mainProcessSpawnsAChildInABusySendLoopAndExitsNormally :: TestTree
test_mainProcessSpawnsAChildInABusySendLoopAndExitsNormally =
  localOption
  (timeoutSeconds 300)
  (testCase "spawn a child with a busy send loop and exit normally"
   (Scheduler.defaultMain
     (do void (spawn (forever (void (sendMessage forkIoScheduler 1000 (toDyn "test")))))
         void (exitNormally forkIoScheduler)
         fail "This should not happen!!"
     )))


test_mainProcessSpawnsAChildBothReturn :: TestTree
test_mainProcessSpawnsAChildBothReturn =
  localOption
  (timeoutSeconds 30)
  (testCase "spawn a child and let it return and return"
   (Scheduler.defaultMain
     (do child <- spawn (void (receiveMessageAs @String forkIoScheduler))
         True <- sendMessage forkIoScheduler child (toDyn "test")
         return ()
     )))

test_mainProcessSpawnsAChildBothExitNormally :: TestTree
test_mainProcessSpawnsAChildBothExitNormally =
  localOption
  (timeoutSeconds 30)
  (testCase "spawn a child and let it exit and exit"
   (Scheduler.defaultMain
     (do child <- spawn
                 (do void (receiveMessageAs @String forkIoScheduler)
                     void (exitNormally forkIoScheduler)
                     error "This should not happen (child)!!"
                 )
         True <- sendMessage forkIoScheduler child (toDyn "test")
         void (exitNormally forkIoScheduler)
         error "This should not happen!!"
     )))