immutaball-core-0.1.0.4.1: Test/Immutaball/Share/State/Fixtures.hs
{-# OPTIONS_GHC -fno-warn-tabs #-} -- Support tab indentation better, for a better default of no warning if tabs are used: https://dmitryfrank.com/articles/indent_with_tabs_align_with_spaces .
-- Enable warnings:
{-# OPTIONS_GHC -Wall -fno-warn-tabs #-}
-- Test.hs.
{-# LANGUAGE Haskell2010 #-}
{-# LANGUAGE TemplateHaskell #-}
module Test.Immutaball.Share.State.Fixtures
(
withImmutaball,
withImmutaball',
exclusively,
exclusivelyUnsafeMutex,
ImmutaballFixture(..), ibfSDLManager,
unsafeGlobalImmutaballFixture,
unsafeGlobalImmutaballFixtureInitializerMutex,
immutaballFixture,
immutaballFixture',
initializeImmutaballFixture,
freeImmutaballFixture
) where
import Prelude ()
import Immutaball.Prelude
import Control.Exception
import Control.Monad
import Data.Maybe
import Control.Concurrent.STM.TMVar
import Control.Lens
import Control.Monad.STM
import qualified Immutaball.Ball.CLI as CLI
import Immutaball.Share.Config
import Immutaball.Share.ImmutaballIO
import Immutaball.Share.SDLManager
import Immutaball.Share.State
import Immutaball.Share.Utils
import System.IO.Unsafe (unsafePerformIO)
-- ImmutaballFixture moved to fix TH errors.
data ImmutaballFixture = ImmutaballFixture {
_ibfSDLManager :: Maybe SDLManagerHandle
}
makeLenses ''ImmutaballFixture
withImmutaball :: (TMVar a -> IBContext -> Immutaball) -> [String] -> IO a
withImmutaball = withImmutaball' True
withImmutaball' :: Bool -> (TMVar a -> IBContext -> Immutaball) -> [String] -> IO a
withImmutaball' headless immutaball extraArgs = do
mout <- atomically $ newEmptyTMVar
ibf <- immutaballFixture headless
let x'cfg = defaultStaticConfig &
x'cfgInitialWireWithCxt .~ Just (\cxt -> Just $ immutaball mout cxt) &
x'cfgUseExistingSDLManager .~ (ibf^.ibfSDLManager)
let args = if' headless ["--headless"] [] ++ extraArgs
runImmutaballIO $ CLI.immutaballWithArgs x'cfg args
out <- atomically $ takeTMVar mout
return out
-- | Tasty provides no feature for mutually exclusive tests, but we only want
-- one SDL instance running at a time.
--
-- We would create a TMVar in a setup IO, but tasty does not seem to provide
-- this feature either unfortunately.
-- So just do it in the static context with unsafePerformIO.
exclusively :: IO a -> IO a
exclusively m = do
() <- atomically $ takeTMVar exclusivelyUnsafeMutex
m `finally` do
atomically $ putTMVar exclusivelyUnsafeMutex ()
{-# NOINLINE exclusivelyUnsafeMutex #-}
exclusivelyUnsafeMutex :: TMVar ()
exclusivelyUnsafeMutex = unsafePerformIO $ newTMVarIO ()
-- ImmutaballFixture moved to fix TH errors.
{-# NOINLINE unsafeGlobalImmutaballFixture #-}
unsafeGlobalImmutaballFixture :: TMVar ImmutaballFixture
unsafeGlobalImmutaballFixture = unsafePerformIO $ newEmptyTMVarIO
{-# NOINLINE unsafeGlobalImmutaballFixtureInitializerMutex #-}
unsafeGlobalImmutaballFixtureInitializerMutex :: TMVar ()
unsafeGlobalImmutaballFixtureInitializerMutex = unsafePerformIO $ newTMVarIO ()
immutaballFixture :: Bool -> IO ImmutaballFixture
immutaballFixture = immutaballFixture' True
immutaballFixture' :: Bool -> Bool -> IO ImmutaballFixture
immutaballFixture' sharedSDLManager headless = do
mibf <- atomically $ do
() <- takeTMVar unsafeGlobalImmutaballFixtureInitializerMutex
mibf <- tryReadTMVar unsafeGlobalImmutaballFixture
when (isJust mibf) $ do
putTMVar unsafeGlobalImmutaballFixtureInitializerMutex ()
return mibf
case mibf of
Just ibf -> return ibf
Nothing -> do
ibf <- initializeImmutaballFixture sharedSDLManager headless
atomically $ do
writeTMVar unsafeGlobalImmutaballFixture ibf
putTMVar unsafeGlobalImmutaballFixtureInitializerMutex ()
return ibf
initializeImmutaballFixture :: Bool -> Bool -> IO ImmutaballFixture
initializeImmutaballFixture sharedSDLManager headless = do
mhandle <- atomically $ newEmptyTMVar
msdlManager <- if' (not sharedSDLManager) (return Nothing) $ do
runImmutaballIO . initSDLManager headless $ \h -> mkAtomically (putTMVar mhandle h) $ \() -> mkEmptyIBIO
atomically $ Just <$> takeTMVar mhandle
return $ ImmutaballFixture {
_ibfSDLManager = msdlManager
}
freeImmutaballFixture :: ImmutaballFixture -> IO ()
freeImmutaballFixture ibf = do
maybe (return ()) (runImmutaballIO . quitSDLManager) (ibf^.ibfSDLManager)
return ()