1{-# LANGUAGE MultiParamTypeClasses #-}
2{-# LANGUAGE FlexibleInstances #-}
3-- Generate some input bytes and delays between blocks of input bytes.
4-- Verify the events produced are as expected.
7import Verify.Graphics.Vty.Output
9import Graphics.Vty hiding (resize)
10import Graphics.Vty.Input.Events
11import Graphics.Vty.Input.Loop
12import Graphics.Vty.Input.Terminfo
14import Control.Concurrent
15import Control.Concurrent.STM
16import Control.Exception
17import Lens.Micro ((^.))
21import Data.List (intersperse, reverse, nubBy)
23import System.Console.Terminfo
25import System.Posix.Terminal (openPseudoTerminal)
26import System.Posix.Types
29import Test.Framework.Providers.SmallCheck
32import Test.SmallCheck.Series
36-- processing a block of 16 chars is the largest I can do without taking
37-- too long to run the test.
44forEachOf :: (Show a, Testable m b) => [a] -> (a -> b) -> Property m
45forEachOf l = over (generate (\n -> take n l))
49 -- ^ Input sequence encoded as a string. Regardless, the input is
50 -- read a byte at a time.
52 -- ^ Microsecond delay
55type InputSpec = [InputEvent]
57type ExpectedSpec = [Event]
59synthesizeInput :: InputSpec -> Fd -> IO ()
60synthesizeInput input outHandle = forM_ input f >> (void $ fdWrite outHandle "\xFFFD")
62 f (Bytes str) = void $ fdWrite outHandle str
63 f (Delay t) = threadDelay t
65minDetectableDelay :: Int
66minDetectableDelay = 4000
72testKeyDelay = minDetectableDelay * 4
74testEscSampleDelay :: Int
75testEscSampleDelay = minDetectableDelay * 2
77genEventsUsingIoActions :: Int -> IO () -> IO () -> IO ()
78genEventsUsingIoActions maxDuration inputAction outputAction = do
79 let maxDuration' = max minTimout maxDuration
80 readComplete <- newEmptyMVar
81 writeComplete <- newEmptyMVar
82 _ <- forkOS $ inputAction `finally` putMVar writeComplete ()
83 _ <- forkOS $ outputAction `finally` putMVar readComplete ()
84 Just () <- timeout maxDuration' $ takeMVar writeComplete
85 Just () <- timeout maxDuration' $ takeMVar readComplete
88compareEvents :: (Show a1, Show a, Eq a1) => a -> [a1] -> [a1] -> IO Bool
89compareEvents inputSpec expectedEvents outEvents = compareEvents' expectedEvents outEvents
91 compareEvents' [] [] = return True
92 compareEvents' [] outEvents' = do
93 printf "extra events %s\n" (show outEvents') :: IO ()
95 compareEvents' expectedEvents' [] = do
96 printf "events %s were not produced for input %s\n" (show expectedEvents') (show inputSpec) :: IO ()
97 printf "expected events %s\n" (show expectedEvents) :: IO ()
98 printf "received events %s\n" (show outEvents) :: IO ()
100 compareEvents' (e : expectedEvents') (o : outEvents')
101 | e == o = compareEvents' expectedEvents' outEvents'
103 printf "%s expected not %s for input %s\n" (show e) (show o) (show inputSpec) :: IO ()
104 printf "expected events %s\n" (show expectedEvents) :: IO ()
105 printf "received events %s\n" (show outEvents) :: IO ()
108assertEventsFromSynInput :: ClassifyMap -> InputSpec -> ExpectedSpec -> IO Bool
109assertEventsFromSynInput table inputSpec expectedEvents = do
110 let maxDuration = sum [t | Delay t <- inputSpec] + minDetectableDelay
111 eventCount = length expectedEvents
112 (writeFd, readFd) <- openPseudoTerminal
113 (setTermAttr,_) <- attributeControl readFd
115 let testConfig = defaultConfig { inputFd = Just readFd
116 , termName = Just "dummy"
120 input <- initInput testConfig table
121 eventsRef <- newIORef []
122 let writeWaitClose = do
123 synthesizeInput inputSpec writeFd
124 threadDelay minDetectableDelay
126 threadDelay minDetectableDelay
130 let readEvents = readLoop eventCount
131 readLoop 0 = return ()
133 e <- atomically $ readTChan $ input^.eventChannel
134 modifyIORef eventsRef ((:) e)
136 genEventsUsingIoActions maxDuration writeWaitClose readEvents
137 outEvents <- reverse <$> readIORef eventsRef
138 compareEvents inputSpec expectedEvents outEvents
140newtype InputBlocksUsingTable event
141 = InputBlocksUsingTable ([(String,event)] -> [(String, event)])
143instance Show (InputBlocksUsingTable event) where
144 show (InputBlocksUsingTable _g) = "InputBlocksUsingTable"
146instance Monad m => Serial m (InputBlocksUsingTable event) where
148 n :: Int <- localDepth (const maxTableSize) series
149 return $ InputBlocksUsingTable $ \raw_table ->
150 let table = reverse $ nubBy (\(s0,_) (s1,_) -> s0 == s1) $ reverse raw_table
151 in concat (take n (selections table))
154 selections (x:xs) = let z = selections xs in [x] : (z ++ map ((:) x) z)
156verifyVisibleSynInputToEvent :: Property IO
157verifyVisibleSynInputToEvent = forAll $ \(InputBlocksUsingTable gen) -> monadic $ do
158 let table = visibleChars
160 events = map snd inputSeq
161 keydowns = map (Bytes . fst) inputSeq
162 input = intersperse (Delay testKeyDelay) keydowns ++ [Delay testKeyDelay]
163 assertEventsFromSynInput universalTable input events
165verifyCapsSynInputToEvent :: Property IO
166verifyCapsSynInputToEvent = forAll $ \(InputBlocksUsingTable gen) ->
167 forEachOf terminalsOfInterest $ \terminalName -> monadic $ do
168 term <- setupTerm terminalName
169 let table = capsClassifyMap term keysFromCapsTable
171 events = map snd inputSeq
172 keydowns = map (Bytes . fst) inputSeq
173 input = intersperse (Delay testKeyDelay) keydowns ++ [Delay testKeyDelay]
174 assertEventsFromSynInput table input events
176verifySpecialSynInputToEvent :: Property IO
177verifySpecialSynInputToEvent = forAll $ \(InputBlocksUsingTable gen) -> monadic $ do
178 let table = specialSupportKeys
180 events = map snd inputSeq
181 keydowns = map (Bytes . fst) inputSeq
182 input = intersperse (Delay testKeyDelay) keydowns ++ [Delay testKeyDelay]
183 assertEventsFromSynInput universalTable input events
185verifyFullSynInputToEvent :: Property IO
186verifyFullSynInputToEvent = forAll $ \(InputBlocksUsingTable gen) ->
187 forEachOf terminalsOfInterest $ \terminalName -> monadic $ do
188 term <- setupTerm terminalName
189 let table = classifyMapForTerm terminalName term
191 events = map snd inputSeq
192 keydowns = map (Bytes . fst) inputSeq
193 input = intersperse (Delay testKeyDelay) keydowns ++ [Delay testKeyDelay]
194 assertEventsFromSynInput table input events
196verifyFullSynInputToEvent_2x :: Property IO
197verifyFullSynInputToEvent_2x = forAll $ \(InputBlocksUsingTable gen) ->
198 forEachOf terminalsOfInterest $ \terminalName -> monadic $ do
199 term <- setupTerm terminalName
200 let table = classifyMapForTerm terminalName term
202 events = concatMap ((\s -> [s,s]) . snd) inputSeq
203 keydowns = map (Bytes . (\s -> s ++ s) . fst) inputSeq
204 input = intersperse (Delay testKeyDelay) keydowns ++ [Delay testKeyDelay]
205 assertEventsFromSynInput table input events
209 [ testProperty "synthesized typing of single visible chars translates to expected events"
210 verifyVisibleSynInputToEvent
211 , testProperty "synthesized typing of keys from capabilities tables translates to expected events"
212 verifyCapsSynInputToEvent
213 , testProperty "synthesized typing of hard coded special keys translates to expected events"
214 verifySpecialSynInputToEvent
215 , testProperty "synthesized typing of any key in the table translates to its paired event"
216 verifyFullSynInputToEvent
217 , testProperty "synthesized typing of 2x any key in the table translates to 2x paired event"
218 verifyFullSynInputToEvent_2x
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list