packages feed

cleff-0.3.3.0: test/ConcurrencySpec.hs

module ConcurrencySpec where

import           Cleff
import           Cleff.Error         (runError, throwError)
import           Cleff.State
import           Control.Monad       (void, when)
import           Data.Set            (Set)
import qualified Data.Set            as Set
import           Test.Hspec
import           UnliftIO            (concurrently_, replicateConcurrently_)
import           UnliftIO.Concurrent (threadDelay)

spec :: Spec
spec = do
  sharedState
  localState
  errorHandling

sharedState :: Spec
sharedState = it "should have shared state" do
  (_, set) <- runIOE $ runState (Set.empty @Int) do
    concurrently_ (addWhen even x) (addWhen odd x)
  set `shouldBe` Set.fromList [1..x]
  where
    x :: Int = 100
    addWhen :: State (Set Int) :> es => (Int -> Bool) -> Int -> Eff es ()
    addWhen f = \case
      0 -> pure ()
      n -> do
        when (f n) $ do
          modify $ Set.insert n
        addWhen f $ n - 1

localState :: Spec
localState = it "should have local state" $
  void $ runIOE $ runStateLocal x $ do
    replicateConcurrently_ 2 $ do
      r <- goDownward 0
      liftIO $ r `shouldBe` x
  where
    x :: Int
    x = 100000

    goDownward :: State Int :> es => Int -> Eff es Int
    goDownward acc = do
      end <- state @Int $ \case
        0 -> (True,  0)
        n -> (False, n - 1)
      if end
        then pure acc
        else goDownward $ acc + 1

errorHandling :: Spec
errorHandling = it "should handle errors properly" do
  (r, s) <- runIOE $ runState (0 :: Int) $ runError @String $ concurrently_
    (liftIO (threadDelay 10000) >> throwError err)
    (modify (+x))
  case r of
    Left e  -> e `shouldBe` err
    Right _ -> expectationFailure "no error caught - or error escaped"
  s `shouldBe` x
  where
    x :: Int = 67
    err = "error"