packages feed

context-0.2.0.1: test-suite/Test/Context/ConcurrentSpec.hs

{-# 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