nano-ui-0.1.0.0: test/integration/Cases/Runner.hs
module Cases.Runner (runSessionLoopTest, runDrawingLockTest) where
import Control.Exception
( IOException
, MaskingState (Unmasked)
, getMaskingState
, throwIO
, try
)
import Data.IORef (IORef, atomicModifyIORef', modifyIORef', newIORef, readIORef)
import NanoUI (Input (..), V2 (..), emptyInput)
import NanoUI.Debug (newDebugSampler)
import NanoUI.Runner
import NanoUI.Testing (Context, clearDirty)
import NanoUI.Testing.Assert (assertEq)
-- Exercise queued edges, a dirty follow-up frame, skipped input, and blocking
-- waits without requiring a native window or depending on wall-clock timing.
runSessionLoopTest :: Context -> IORef Int -> IO ()
runSessionLoopTest ctx failed = do
logRef <- newIORef []
waits <- newIORef [(-1, [1, 2]), (0, []), (-1, []), (-1, [3 :: Int])]
draws <- newIORef (0 :: Int)
debug <- newDebugSampler
let
note message = modifyIORef' logRef (<> [message])
driver =
SessionDriver
{ sdPollEvents = do
note "poll"
pure []
, sdWaitEvents = \timeout -> do
note ("wait " <> show timeout)
(expected, events) <- atomicModifyIORef' waits $ \batches -> case batches of
batch : rest -> (rest, batch)
[] -> ([], (timeout, [3]))
assertEq failed expected timeout
pure events
, sdApplyEvent = \inp event -> inp {inputMousePos = V2 (fromIntegral event) 0}
, sdIsButtonEdge = const True
, sdIsHardQuit = const False
, sdIsSessionQuit = (== 3)
, sdSyncDisplay = \c inp -> pure (c, inp)
, sdDebug = debug
, sdContinuous = False
, sdPacingMs = 16
, sdPresentPaces = pure False
, sdAlignSec = 0
, sdShouldDraw = \_ previous current _ _ -> do
note ("decide " <> show (inputMousePos previous, inputMousePos current))
pure (inputMousePos current == V2 1 0)
, sdDraw = \_ inp _ -> do
n <- atomicModifyIORef' draws (\n -> (n + 1, n + 1))
note "draw"
pure (n <= 2, inp {inputMousePos = V2 10 0})
, sdOnCursor = \_ _ -> note "cursor"
, sdShouldQuit = const False
}
-- A new context starts dirty, which would make the first wait immediate.
clearDirty ctx
runSessionLoop driver ctx emptyInput
actual <- readIORef logRef
assertEq
failed
[ "wait -1"
, "decide " <> show (V2 0 0, V2 1 0)
, "draw"
, "cursor"
, "draw"
, "cursor"
, "poll"
, "wait 0"
, "draw"
, "cursor"
, "wait -1"
, "decide " <> show (V2 10 0, V2 10 0)
, "cursor"
, "wait -1"
]
actual
assertEq failed 3 =<< readIORef draws
runDrawingLockTest :: Context -> IORef Int -> IO ()
runDrawingLockTest _ failed = do
lock <- newDrawingLock
result <- tryWithDrawingLock lock $ do
assertEq failed Unmasked =<< getMaskingState
assertEq failed Nothing =<< tryWithDrawingLock lock (pure ())
assertEq failed (Just ()) result
failure <-
try (tryWithDrawingLock lock (throwIO (userError "draw failed"))) ::
IO (Either IOException (Maybe ()))
assertEq failed True (either (const True) (const False) failure)
assertEq failed (Just ()) =<< tryWithDrawingLock lock (pure ())