{-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE TypeApplications #-} module Test.Context.ConcurrentSpec ( spec ) where import Prelude import Test.Hspec import qualified Context import qualified Control.Monad as Monad data Thing = Thing { stuff :: Int } deriving stock (Eq, Show) spec :: Spec spec = do describe "forkIO" $ do describe "mineMay" $ do it "empty stores" $ do threadDone <- Context.newEmptyMVar Context.withEmptyStore @IO @Thing $ \store1 -> do Context.withEmptyStore @IO @Char $ \store2 -> do Monad.void $ Context.forkIO $ do Context.mineMay store1 `shouldReturn` Nothing Context.mineMay store2 `shouldReturn` Nothing Context.putMVar threadDone () Context.takeMVar threadDone it "initially-empty stores with registered context" $ do threadDone <- Context.newEmptyMVar Context.withEmptyStore $ \store1 -> do Context.use store1 Thing { stuff = 1 } $ do Context.withEmptyStore $ \store2 -> do Context.use store2 'a' $ do Monad.void $ Context.forkIO $ do Context.mineMay store1 `shouldReturn` Just Thing { stuff = 1 } Context.mineMay store2 `shouldReturn` Just 'a' Context.putMVar threadDone () Context.takeMVar threadDone it "initially-empty stores with nested contexts" $ do threadDone <- Context.newEmptyMVar Context.withEmptyStore $ \store1 -> do Context.use store1 Thing { stuff = 1 } $ do Context.use store1 Thing { stuff = 2 } $ do Context.withEmptyStore $ \store2 -> do Context.use store2 'a' $ do Context.use store2 'b' $ do Monad.void $ Context.forkIO $ do Context.mineMay store1 `shouldReturn` Just Thing { stuff = 2 } Context.mineMay store2 `shouldReturn` Just 'b' Context.putMVar threadDone () Context.takeMVar threadDone it "non-empty stores" $ do threadDone <- Context.newEmptyMVar Context.withNonEmptyStore Thing { stuff = 1 } $ \store1 -> do Context.withNonEmptyStore 'a' $ \store2 -> do Monad.void $ Context.forkIO $ do Context.mineMay store1 `shouldReturn` Just Thing { stuff = 1 } Context.mineMay store2 `shouldReturn` Just 'a' Context.putMVar threadDone () Context.takeMVar threadDone describe "forkFinally" $ do describe "mineMay" $ do it "empty stores" $ do threadDone <- Context.newEmptyMVar Context.withEmptyStore @IO @Thing $ \store1 -> do Context.withEmptyStore @IO @Char $ \store2 -> do let checkStores = do Context.mineMay store1 `shouldReturn` Nothing Context.mineMay store2 `shouldReturn` Nothing Monad.void $ Context.forkFinally checkStores $ \_eResult -> do checkStores Context.putMVar threadDone () Context.takeMVar threadDone it "initially-empty stores with registered context" $ do threadDone <- Context.newEmptyMVar Context.withEmptyStore $ \store1 -> do Context.use store1 Thing { stuff = 1 } $ do Context.withEmptyStore $ \store2 -> do Context.use store2 'a' $ do let checkStores = do Context.mineMay store1 `shouldReturn` Just Thing { stuff = 1 } Context.mineMay store2 `shouldReturn` Just 'a' Monad.void $ Context.forkFinally checkStores $ \_eResult -> do checkStores Context.putMVar threadDone () Context.takeMVar threadDone it "initially-empty stores with nested contexts" $ do threadDone <- Context.newEmptyMVar Context.withEmptyStore $ \store1 -> do Context.use store1 Thing { stuff = 1 } $ do Context.use store1 Thing { stuff = 2 } $ do Context.withEmptyStore $ \store2 -> do Context.use store2 'a' $ do Context.use store2 'b' $ do let checkStores = do Context.mineMay store1 `shouldReturn` Just Thing { stuff = 2 } Context.mineMay store2 `shouldReturn` Just 'b' Monad.void $ Context.forkFinally checkStores $ \_eResult -> do checkStores Context.putMVar threadDone () Context.takeMVar threadDone it "non-empty stores" $ do threadDone <- Context.newEmptyMVar Context.withNonEmptyStore Thing { stuff = 1 } $ \store1 -> do Context.withNonEmptyStore 'a' $ \store2 -> do let checkStores = do Context.mineMay store1 `shouldReturn` Just Thing { stuff = 1 } Context.mineMay store2 `shouldReturn` Just 'a' Monad.void $ Context.forkFinally checkStores $ \_eResult -> do checkStores Context.putMVar threadDone () Context.takeMVar threadDone describe "forkIOWithUnmask" $ do describe "mineMay" $ do it "empty stores" $ do threadDone <- Context.newEmptyMVar Context.withEmptyStore @IO @Thing $ \store1 -> do Context.withEmptyStore @IO @Char $ \store2 -> do Monad.void $ Context.forkIOWithUnmask $ \_restore -> do Context.mineMay store1 `shouldReturn` Nothing Context.mineMay store2 `shouldReturn` Nothing Context.putMVar threadDone () Context.takeMVar threadDone it "initially-empty stores with registered context" $ do threadDone <- Context.newEmptyMVar Context.withEmptyStore $ \store1 -> do Context.use store1 Thing { stuff = 1 } $ do Context.withEmptyStore $ \store2 -> do Context.use store2 'a' $ do Monad.void $ Context.forkIOWithUnmask $ \_restore -> do Context.mineMay store1 `shouldReturn` Just Thing { stuff = 1 } Context.mineMay store2 `shouldReturn` Just 'a' Context.putMVar threadDone () Context.takeMVar threadDone it "initially-empty stores with nested contexts" $ do threadDone <- Context.newEmptyMVar Context.withEmptyStore $ \store1 -> do Context.use store1 Thing { stuff = 1 } $ do Context.use store1 Thing { stuff = 2 } $ do Context.withEmptyStore $ \store2 -> do Context.use store2 'a' $ do Context.use store2 'b' $ do Monad.void $ Context.forkIOWithUnmask $ \_restore -> do Context.mineMay store1 `shouldReturn` Just Thing { stuff = 2 } Context.mineMay store2 `shouldReturn` Just 'b' Context.putMVar threadDone () Context.takeMVar threadDone it "non-empty stores" $ do threadDone <- Context.newEmptyMVar Context.withNonEmptyStore Thing { stuff = 1 } $ \store1 -> do Context.withNonEmptyStore 'a' $ \store2 -> do Monad.void $ Context.forkIOWithUnmask $ \_restore -> do Context.mineMay store1 `shouldReturn` Just Thing { stuff = 1 } Context.mineMay store2 `shouldReturn` Just 'a' Context.putMVar threadDone () Context.takeMVar threadDone describe "forkOn" $ do describe "mineMay" $ do it "empty stores" $ do threadDone <- Context.newEmptyMVar Context.withEmptyStore @IO @Thing $ \store1 -> do Context.withEmptyStore @IO @Char $ \store2 -> do Monad.void $ Context.forkOn 1 $ do Context.mineMay store1 `shouldReturn` Nothing Context.mineMay store2 `shouldReturn` Nothing Context.putMVar threadDone () Context.takeMVar threadDone it "initially-empty stores with registered context" $ do threadDone <- Context.newEmptyMVar Context.withEmptyStore $ \store1 -> do Context.use store1 Thing { stuff = 1 } $ do Context.withEmptyStore $ \store2 -> do Context.use store2 'a' $ do Monad.void $ Context.forkOn 1 $ do Context.mineMay store1 `shouldReturn` Just Thing { stuff = 1 } Context.mineMay store2 `shouldReturn` Just 'a' Context.putMVar threadDone () Context.takeMVar threadDone it "initially-empty stores with nested contexts" $ do threadDone <- Context.newEmptyMVar Context.withEmptyStore $ \store1 -> do Context.use store1 Thing { stuff = 1 } $ do Context.use store1 Thing { stuff = 2 } $ do Context.withEmptyStore $ \store2 -> do Context.use store2 'a' $ do Context.use store2 'b' $ do Monad.void $ Context.forkOn 1 $ do Context.mineMay store1 `shouldReturn` Just Thing { stuff = 2 } Context.mineMay store2 `shouldReturn` Just 'b' Context.putMVar threadDone () Context.takeMVar threadDone it "non-empty stores" $ do threadDone <- Context.newEmptyMVar Context.withNonEmptyStore Thing { stuff = 1 } $ \store1 -> do Context.withNonEmptyStore 'a' $ \store2 -> do Monad.void $ Context.forkOn 1 $ do Context.mineMay store1 `shouldReturn` Just Thing { stuff = 1 } Context.mineMay store2 `shouldReturn` Just 'a' Context.putMVar threadDone () Context.takeMVar threadDone describe "forkOnWithUnmask" $ do describe "mineMay" $ do it "empty stores" $ do threadDone <- Context.newEmptyMVar Context.withEmptyStore @IO @Thing $ \store1 -> do Context.withEmptyStore @IO @Char $ \store2 -> do Monad.void $ Context.forkOnWithUnmask 1 $ \_restore -> do Context.mineMay store1 `shouldReturn` Nothing Context.mineMay store2 `shouldReturn` Nothing Context.putMVar threadDone () Context.takeMVar threadDone it "initially-empty stores with registered context" $ do threadDone <- Context.newEmptyMVar Context.withEmptyStore $ \store1 -> do Context.use store1 Thing { stuff = 1 } $ do Context.withEmptyStore $ \store2 -> do Context.use store2 'a' $ do Monad.void $ Context.forkOnWithUnmask 1 $ \_restore -> do Context.mineMay store1 `shouldReturn` Just Thing { stuff = 1 } Context.mineMay store2 `shouldReturn` Just 'a' Context.putMVar threadDone () Context.takeMVar threadDone it "initially-empty stores with nested contexts" $ do threadDone <- Context.newEmptyMVar Context.withEmptyStore $ \store1 -> do Context.use store1 Thing { stuff = 1 } $ do Context.use store1 Thing { stuff = 2 } $ do Context.withEmptyStore $ \store2 -> do Context.use store2 'a' $ do Context.use store2 'b' $ do Monad.void $ Context.forkOnWithUnmask 1 $ \_restore -> do Context.mineMay store1 `shouldReturn` Just Thing { stuff = 2 } Context.mineMay store2 `shouldReturn` Just 'b' Context.putMVar threadDone () Context.takeMVar threadDone it "non-empty stores" $ do threadDone <- Context.newEmptyMVar Context.withNonEmptyStore Thing { stuff = 1 } $ \store1 -> do Context.withNonEmptyStore 'a' $ \store2 -> do Monad.void $ Context.forkOnWithUnmask 1 $ \_restore -> do Context.mineMay store1 `shouldReturn` Just Thing { stuff = 1 } Context.mineMay store2 `shouldReturn` Just 'a' Context.putMVar threadDone () Context.takeMVar threadDone describe "forkOS" $ do describe "mineMay" $ do it "empty stores" $ do threadDone <- Context.newEmptyMVar Context.withEmptyStore @IO @Thing $ \store1 -> do Context.withEmptyStore @IO @Char $ \store2 -> do Monad.void $ Context.forkOS $ do Context.mineMay store1 `shouldReturn` Nothing Context.mineMay store2 `shouldReturn` Nothing Context.putMVar threadDone () Context.takeMVar threadDone it "initially-empty stores with registered context" $ do threadDone <- Context.newEmptyMVar Context.withEmptyStore $ \store1 -> do Context.use store1 Thing { stuff = 1 } $ do Context.withEmptyStore $ \store2 -> do Context.use store2 'a' $ do Monad.void $ Context.forkOS $ do Context.mineMay store1 `shouldReturn` Just Thing { stuff = 1 } Context.mineMay store2 `shouldReturn` Just 'a' Context.putMVar threadDone () Context.takeMVar threadDone it "initially-empty stores with nested contexts" $ do threadDone <- Context.newEmptyMVar Context.withEmptyStore $ \store1 -> do Context.use store1 Thing { stuff = 1 } $ do Context.use store1 Thing { stuff = 2 } $ do Context.withEmptyStore $ \store2 -> do Context.use store2 'a' $ do Context.use store2 'b' $ do Monad.void $ Context.forkOS $ do Context.mineMay store1 `shouldReturn` Just Thing { stuff = 2 } Context.mineMay store2 `shouldReturn` Just 'b' Context.putMVar threadDone () Context.takeMVar threadDone it "non-empty stores" $ do threadDone <- Context.newEmptyMVar Context.withNonEmptyStore Thing { stuff = 1 } $ \store1 -> do Context.withNonEmptyStore 'a' $ \store2 -> do Monad.void $ Context.forkOS $ do Context.mineMay store1 `shouldReturn` Just Thing { stuff = 1 } Context.mineMay store2 `shouldReturn` Just 'a' Context.putMVar threadDone () Context.takeMVar threadDone describe "forkOSWithUnmask" $ do describe "mineMay" $ do it "empty stores" $ do threadDone <- Context.newEmptyMVar Context.withEmptyStore @IO @Thing $ \store1 -> do Context.withEmptyStore @IO @Char $ \store2 -> do Monad.void $ Context.forkOSWithUnmask $ \_restore -> do Context.mineMay store1 `shouldReturn` Nothing Context.mineMay store2 `shouldReturn` Nothing Context.putMVar threadDone () Context.takeMVar threadDone it "initially-empty stores with registered context" $ do threadDone <- Context.newEmptyMVar Context.withEmptyStore $ \store1 -> do Context.use store1 Thing { stuff = 1 } $ do Context.withEmptyStore $ \store2 -> do Context.use store2 'a' $ do Monad.void $ Context.forkOSWithUnmask $ \_restore -> do Context.mineMay store1 `shouldReturn` Just Thing { stuff = 1 } Context.mineMay store2 `shouldReturn` Just 'a' Context.putMVar threadDone () Context.takeMVar threadDone it "initially-empty stores with nested contexts" $ do threadDone <- Context.newEmptyMVar Context.withEmptyStore $ \store1 -> do Context.use store1 Thing { stuff = 1 } $ do Context.use store1 Thing { stuff = 2 } $ do Context.withEmptyStore $ \store2 -> do Context.use store2 'a' $ do Context.use store2 'b' $ do Monad.void $ Context.forkOSWithUnmask $ \_restore -> do Context.mineMay store1 `shouldReturn` Just Thing { stuff = 2 } Context.mineMay store2 `shouldReturn` Just 'b' Context.putMVar threadDone () Context.takeMVar threadDone it "non-empty stores" $ do threadDone <- Context.newEmptyMVar Context.withNonEmptyStore Thing { stuff = 1 } $ \store1 -> do Context.withNonEmptyStore 'a' $ \store2 -> do Monad.void $ Context.forkOSWithUnmask $ \_restore -> do Context.mineMay store1 `shouldReturn` Just Thing { stuff = 1 } Context.mineMay store2 `shouldReturn` Just 'a' Context.putMVar threadDone () Context.takeMVar threadDone describe "runInBoundThread" $ do describe "mineMay" $ do it "empty stores" $ do threadDone <- Context.newEmptyMVar Context.withEmptyStore @IO @Thing $ \store1 -> do Context.withEmptyStore @IO @Char $ \store2 -> do Context.runInBoundThread $ do Context.mineMay store1 `shouldReturn` Nothing Context.mineMay store2 `shouldReturn` Nothing Context.putMVar threadDone () Context.takeMVar threadDone it "initially-empty stores with registered context" $ do threadDone <- Context.newEmptyMVar Context.withEmptyStore $ \store1 -> do Context.use store1 Thing { stuff = 1 } $ do Context.withEmptyStore $ \store2 -> do Context.use store2 'a' $ do Context.runInBoundThread $ do Context.mineMay store1 `shouldReturn` Just Thing { stuff = 1 } Context.mineMay store2 `shouldReturn` Just 'a' Context.putMVar threadDone () Context.takeMVar threadDone it "initially-empty stores with nested contexts" $ do threadDone <- Context.newEmptyMVar Context.withEmptyStore $ \store1 -> do Context.use store1 Thing { stuff = 1 } $ do Context.use store1 Thing { stuff = 2 } $ do Context.withEmptyStore $ \store2 -> do Context.use store2 'a' $ do Context.use store2 'b' $ do Context.runInBoundThread $ do Context.mineMay store1 `shouldReturn` Just Thing { stuff = 2 } Context.mineMay store2 `shouldReturn` Just 'b' Context.putMVar threadDone () Context.takeMVar threadDone it "non-empty stores" $ do threadDone <- Context.newEmptyMVar Context.withNonEmptyStore Thing { stuff = 1 } $ \store1 -> do Context.withNonEmptyStore 'a' $ \store2 -> do Context.runInBoundThread $ do Context.mineMay store1 `shouldReturn` Just Thing { stuff = 1 } Context.mineMay store2 `shouldReturn` Just 'a' Context.putMVar threadDone () Context.takeMVar threadDone describe "runInUnboundThread" $ do describe "mineMay" $ do it "empty stores" $ do threadDone <- Context.newEmptyMVar Context.withEmptyStore @IO @Thing $ \store1 -> do Context.withEmptyStore @IO @Char $ \store2 -> do Context.runInUnboundThread $ do Context.mineMay store1 `shouldReturn` Nothing Context.mineMay store2 `shouldReturn` Nothing Context.putMVar threadDone () Context.takeMVar threadDone it "initially-empty stores with registered context" $ do threadDone <- Context.newEmptyMVar Context.withEmptyStore $ \store1 -> do Context.use store1 Thing { stuff = 1 } $ do Context.withEmptyStore $ \store2 -> do Context.use store2 'a' $ do Context.runInUnboundThread $ do Context.mineMay store1 `shouldReturn` Just Thing { stuff = 1 } Context.mineMay store2 `shouldReturn` Just 'a' Context.putMVar threadDone () Context.takeMVar threadDone it "initially-empty stores with nested contexts" $ do threadDone <- Context.newEmptyMVar Context.withEmptyStore $ \store1 -> do Context.use store1 Thing { stuff = 1 } $ do Context.use store1 Thing { stuff = 2 } $ do Context.withEmptyStore $ \store2 -> do Context.use store2 'a' $ do Context.use store2 'b' $ do Context.runInUnboundThread $ do Context.mineMay store1 `shouldReturn` Just Thing { stuff = 2 } Context.mineMay store2 `shouldReturn` Just 'b' Context.putMVar threadDone () Context.takeMVar threadDone it "non-empty stores" $ do threadDone <- Context.newEmptyMVar Context.withNonEmptyStore Thing { stuff = 1 } $ \store1 -> do Context.withNonEmptyStore 'a' $ \store2 -> do Context.runInUnboundThread $ do Context.mineMay store1 `shouldReturn` Just Thing { stuff = 1 } Context.mineMay store2 `shouldReturn` Just 'a' Context.putMVar threadDone () Context.takeMVar threadDone