1{-# LANGUAGE MultiParamTypeClasses #-}
2{-# LANGUAGE FlexibleInstances #-}
3{- Generate some input bytes and delays between blocks of input bytes. Verify the events produced
8import Verify.Graphics.Vty.Output
10import Graphics.Vty hiding (resize)
11import Graphics.Vty.Input.Events
12import Graphics.Vty.Input.Loop
13import Graphics.Vty.Input.Terminfo
15import Control.Applicative
16import Control.Concurrent
17import Control.Concurrent.STM
18import Control.Exception
19import Lens.Micro ((^.))
24import Data.List (intersperse, reverse, nubBy)
26import System.Console.Terminfo
28import System.Posix.Terminal (openPseudoTerminal)
29import System.Posix.Types
32import Test.Framework.Providers.SmallCheck
35import Test.SmallCheck.Series
39-- processing a block of 16 chars is the largest I can do without taking too long to run the test.
46forEachOf :: (Show a, Testable m b) => [a] -> (a -> b) -> Property m
47forEachOf l = over (generate (\n -> take n l))
50 = Bytes String -- | input sequence encoded as a string. Regardless, the input is read a byte at a time.
51 | Delay Int -- | microsecond delay
54type InputSpec = [InputEvent]
56type ExpectedSpec = [Event]
58synthesizeInput :: InputSpec -> Fd -> IO ()
59synthesizeInput input outHandle = forM_ input f >> (void $ fdWrite outHandle "\xFFFD")
61 f (Bytes str) = void $ fdWrite outHandle str
62 f (Delay t) = threadDelay t
64minDetectableDelay :: Int
65minDetectableDelay = 4000
71testKeyDelay = minDetectableDelay * 4
73testEscSampleDelay :: Int
74testEscSampleDelay = minDetectableDelay * 2
76genEventsUsingIoActions :: Int -> IO () -> IO () -> IO ()
77genEventsUsingIoActions maxDuration inputAction outputAction = do
78 let maxDuration' = max minTimout maxDuration
79 readComplete <- newEmptyMVar
80 writeComplete <- newEmptyMVar
81 _ <- forkOS $ inputAction `finally` putMVar writeComplete ()
82 _ <- forkOS $ outputAction `finally` putMVar readComplete ()
83 Just () <- timeout maxDuration' $ takeMVar writeComplete
84 Just () <- timeout maxDuration' $ takeMVar readComplete
87compareEvents :: (Show a1, Show a, Eq a1) => a -> [a1] -> [a1] -> IO Bool
88compareEvents inputSpec expectedEvents outEvents = compareEvents' expectedEvents outEvents
90 compareEvents' [] [] = return True
91 compareEvents' [] outEvents' = do
92 printf "extra events %s\n" (show outEvents') :: IO ()
94 compareEvents' expectedEvents' [] = do
95 printf "events %s were not produced for input %s\n" (show expectedEvents') (show inputSpec) :: IO ()
96 printf "expected events %s\n" (show expectedEvents) :: IO ()
97 printf "received events %s\n" (show outEvents) :: IO ()
99 compareEvents' (e : expectedEvents') (o : outEvents')
100 | e == o = compareEvents' expectedEvents' outEvents'
102 printf "%s expected not %s for input %s\n" (show e) (show o) (show inputSpec) :: IO ()
103 printf "expected events %s\n" (show expectedEvents) :: IO ()
104 printf "received events %s\n" (show outEvents) :: IO ()
107assertEventsFromSynInput :: ClassifyMap -> InputSpec -> ExpectedSpec -> IO Bool
108assertEventsFromSynInput table inputSpec expectedEvents = do
109 let maxDuration = sum [t | Delay t <- inputSpec] + minDetectableDelay
110 eventCount = length expectedEvents
111 (writeFd, readFd) <- openPseudoTerminal
112 (setTermAttr,_) <- attributeControl readFd
114 let testConfig = def { inputFd = Just readFd
115 , termName = Just "dummy"
119 input <- initInput testConfig table
120 eventsRef <- newIORef []
121 let writeWaitClose = do
122 synthesizeInput inputSpec writeFd
123 threadDelay minDetectableDelay
125 threadDelay minDetectableDelay
129 let readEvents = readLoop eventCount
130 readLoop 0 = return ()
132 e <- atomically $ readTChan $ input^.eventChannel
133 modifyIORef eventsRef ((:) e)
135 genEventsUsingIoActions maxDuration writeWaitClose readEvents
136 outEvents <- reverse <$> readIORef eventsRef
137 compareEvents inputSpec expectedEvents outEvents
139newtype InputBlocksUsingTable event
140 = InputBlocksUsingTable ([(String,event)] -> [(String, event)])
142instance Show (InputBlocksUsingTable event) where
143 show (InputBlocksUsingTable _g) = "InputBlocksUsingTable"
145instance Monad m => Serial m (InputBlocksUsingTable event) where
147 n :: Int <- localDepth (const maxTableSize) series
148 return $ InputBlocksUsingTable $ \raw_table ->
149 let table = reverse $ nubBy (\(s0,_) (s1,_) -> s0 == s1) $ reverse raw_table
150 in concat (take n (selections table))
153 selections (x:xs) = let z = selections xs in [x] : (z ++ map ((:) x) z)
155verifyVisibleSynInputToEvent :: Property IO
156verifyVisibleSynInputToEvent = forAll $ \(InputBlocksUsingTable gen) -> monadic $ do
157 let table = visibleChars
159 events = map snd inputSeq
160 keydowns = map (Bytes . fst) inputSeq
161 input = intersperse (Delay testKeyDelay) keydowns ++ [Delay testKeyDelay]
162 assertEventsFromSynInput universalTable input events
164verifyCapsSynInputToEvent :: Property IO
165verifyCapsSynInputToEvent = forAll $ \(InputBlocksUsingTable gen) ->
166 forEachOf terminalsOfInterest $ \termName -> monadic $ do
167 term <- setupTerm termName
168 let table = capsClassifyMap term keysFromCapsTable
170 events = map snd inputSeq
171 keydowns = map (Bytes . fst) inputSeq
172 input = intersperse (Delay testKeyDelay) keydowns ++ [Delay testKeyDelay]
173 assertEventsFromSynInput table input events
175verifySpecialSynInputToEvent :: Property IO
176verifySpecialSynInputToEvent = forAll $ \(InputBlocksUsingTable gen) -> monadic $ do
177 let table = specialSupportKeys
179 events = map snd inputSeq
180 keydowns = map (Bytes . fst) inputSeq
181 input = intersperse (Delay testKeyDelay) keydowns ++ [Delay testKeyDelay]
182 assertEventsFromSynInput universalTable input events
184verifyFullSynInputToEvent :: Property IO
185verifyFullSynInputToEvent = forAll $ \(InputBlocksUsingTable gen) ->
186 forEachOf terminalsOfInterest $ \termName -> monadic $ do
187 term <- setupTerm termName
188 let table = classifyMapForTerm termName term
190 events = map snd inputSeq
191 keydowns = map (Bytes . fst) inputSeq
192 input = intersperse (Delay testKeyDelay) keydowns ++ [Delay testKeyDelay]
193 assertEventsFromSynInput table input events
195verifyFullSynInputToEvent_2x :: Property IO
196verifyFullSynInputToEvent_2x = forAll $ \(InputBlocksUsingTable gen) ->
197 forEachOf terminalsOfInterest $ \termName -> monadic $ do
198 term <- setupTerm termName
199 let table = classifyMapForTerm termName term
201 events = concatMap ((\s -> [s,s]) . snd) inputSeq
202 keydowns = map (Bytes . (\s -> s ++ s) . fst) inputSeq
203 input = intersperse (Delay testKeyDelay) keydowns ++ [Delay testKeyDelay]
204 assertEventsFromSynInput table input events
208 [ testProperty "synthesized typing of single visible chars translates to expected events"
209 verifyVisibleSynInputToEvent
210 , testProperty "synthesized typing of keys from capabilities tables translates to expected events"
211 verifyCapsSynInputToEvent
212 , testProperty "synthesized typing of hard coded special keys translates to expected events"
213 verifySpecialSynInputToEvent
214 , testProperty "synthesized typing of any key in the table translates to its paired event"
215 verifyFullSynInputToEvent
216 , testProperty "synthesized typing of 2x any key in the table translates to 2x paired event"
217 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