Closed
Description
I had been looking for some info on how to do this and didn't find it, so I figured I'd share my progress here.
This is a function that can start a Brick app using the Graphics.Vty.Output.Mock mock terminal, simulate the given list of Vty.Event
s, and then return the final output of the mock renderer. (Note, the mock vty output is a bit weird; the reference of the weird characters it outputs can be seen in it's source code.)
the test function (click to expand)
runApp :: Ord n => Brick.App s e n -> Vty.DisplayRegion -> s -> List Vty.Event -> IO Text
runApp :: Ord n => Brick.App s e n -> Vty.DisplayRegion -> s -> List Vty.Event -> IO Text
import qualified Brick
import Brick.BChan (newBChan, writeBChan)
import Control.Applicative ((<$>))
import Control.Concurrent (forkIO, newEmptyMVar, putMVar, takeMVar, tryTakeMVar)
import Control.Concurrent.STM (newTChanIO)
import Control.Monad (mapM_)
import Data.IORef (newIORef, readIORef, writeIORef)
import qualified Data.String.UTF8 as UTF8
import qualified Data.Text as Text
import qualified Data.Text.Encoding as Text
import GHC.Err (undefined)
import Graphics.Vty (Vty (Vty))
import qualified Graphics.Vty as Vty
import qualified Graphics.Vty.Output.Mock as VtyMock
data RunAppInternalEvent
= RunAppHalt
runApp :: Ord n => Brick.App s e n -> Vty.DisplayRegion -> s -> List Vty.Event -> IO Text
runApp app displayRegion initialAppState events = do
-- Create concurrent variables and channels
pictureRef <- newIORef Vty.emptyPicture
inputConfigRef <- newIORef undefined
shutdownRef <- newIORef False
eventQueue <- newEmptyMVar
appEventQueue <- newBChan 1
inputEventQueue <- newTChanIO
-- Create the mock vty
(mockData, output) <- VtyMock.mockTerminal displayRegion
let input =
Vty.Input
{ Vty._eventChannel = inputEventQueue,
Vty.shutdownInput = return (),
Vty.restoreInputState = return (),
Vty._configRef = inputConfigRef,
Vty._inputDebug = Nothing
}
let vty =
Vty
{ Vty.update = writeIORef pictureRef,
Vty.nextEvent = takeMVar eventQueue,
Vty.nextEventNonblocking = tryTakeMVar eventQueue,
Vty.inputIface = input,
Vty.outputIface = output,
Vty.refresh = return (),
Vty.shutdown = writeIORef shutdownRef True,
Vty.isShutdown = readIORef shutdownRef
}
-- Start a thread that will send the events
-- and ultimately send a halt event
forkIO $ do
mapM_ (putMVar eventQueue) events
writeBChan appEventQueue RunAppHalt
-- Create and start the app
let wrappedApp =
Brick.App
{ Brick.appDraw = Brick.appDraw app,
Brick.appChooseCursor = Brick.appChooseCursor app,
Brick.appHandleEvent = \case
Brick.VtyEvent e -> Brick.appHandleEvent app $ Brick.VtyEvent e
Brick.AppEvent RunAppHalt -> Brick.halt
Brick.MouseDown n b ms l -> Brick.appHandleEvent app $ Brick.MouseDown n b ms l
Brick.MouseUp n b l -> Brick.appHandleEvent app $ Brick.MouseUp n b l,
Brick.appStartEvent = Brick.appStartEvent app,
Brick.appAttrMap = Brick.appAttrMap app
}
_finalState <-
Brick.customMain
vty
(return vty)
(Just appEventQueue)
wrappedApp
initialAppState
-- Render the final picutre
picture <- readIORef pictureRef
displayContext <- Vty.mkDisplayContext output output displayRegion
Vty.outputPicture displayContext picture
Text.decodeUtf8 . UTF8.toRep <$> readIORef mockData
I'm hoping to eventually clean this up and contribute it to brick itself if there's interest, but there are a few missing pieces still to do:
- have an actual vty
Text
renderer (see Mock render Widgets #405), as theMock
renderer isn't really meant for this use and has an output format that is a bit weird and incomplete for the use of testing apps from an end-user perspective - allow the list of events to contain any Brick Event, or app event, and not be limited to only Vty Events.
Metadata
Metadata
Assignees
Labels
No labels