hgdbmi-0.2: tests/io.hs
{-# LANGUAGE QuasiQuotes #-}
{-
- Test the IO interface of hgdbmi.
-
- Requirements:
- - gdb (see config)
- - gcc (see setup)
-
- Steps:
- - create temporary directory
- - write test program (example)
- - compile test program with debugging
- - start debugger
- - set breakpoint
- - run debugger
- - wait for stop event, evaluate variable and continue (10 times)
- - quit
- - dump log file
-
- Output:
- - stream and notification events (excluding stop events)
- - log file with GDB/MI communication
-}
module Main (main) where
import Control.Concurrent (newEmptyMVar, putMVar, takeMVar, MVar)
import Control.Exception.Base (bracket)
import Control.Monad (forM_, when)
import System.Directory (removeDirectoryRecursive, getCurrentDirectory, setCurrentDirectory)
import System.Exit (ExitCode(..))
import System.IO.Temp (createTempDirectory)
import System.Process (readProcessWithExitCode)
import Text.Printf (printf)
import Paste (paste)
import qualified Gdbmi.IO as G
import qualified Gdbmi.Commands as C
import qualified Gdbmi.Semantics as S
import qualified Gdbmi.Representation as R
config :: G.Config
config = G.Config ["gdb"] (Just "gdb.log")
example :: String
example = [paste|
#include <stdio.h>
void print(int i) {
printf("hello world %d\n", i);
}
int main() {
int i;
for (i=0; ; i++) {
print(i);
}
return 0;
}
|]
callback :: MVar [S.Stopped] -> G.Callback
callback mv = G.Callback print print (Just (putMVar mv))
setup :: IO ()
setup = do
writeFile "example.c" example
(ec, sout, serr) <- readProcessWithExitCode "gcc" (words "-o example -g example.c") ""
case ec of
ExitFailure ec' -> do
putStrLn sout
putStrLn serr
error $ printf "failed to execute gcc: %s" (show ec')
ExitSuccess -> return ()
command :: G.Context -> R.ResultClass -> R.Command -> IO [R.Result]
command ctx rc cmd = do
resp <- G.send_command ctx cmd
let msg = printf "command '%s' failed (%s): %s"
(R.render_command cmd)
(show (R.respClass resp))
((show . S.response_error . R.respResults) resp)
when (R.respClass resp /= rc) (error msg)
return (R.respResults resp)
assert :: (Eq a, Show a) => String -> a -> a -> IO ()
assert what x y = if (x == y)
then return ()
else error $ printf "assertion failed: %s: %s vs. %s" what (show x) (show y)
test :: IO ()
test = do
_ <- setup
mv <- newEmptyMVar
ctx <- G.setup config (callback mv)
let cmd = command ctx
_ <- cmd R.RCDone $ C.cli_command "tty /dev/null"
_ <- cmd R.RCDone $ C.file_exec_and_symbols (Just "example")
let loc = C.file_function_location "example.c" "print"
bp' <- cmd R.RCDone $ C.break_insert False False False False False Nothing Nothing Nothing loc
let (Just bp) = S.response_break_insert bp'
_ <- cmd R.RCRunning $ C.exec_run (Left True)
forM_ [(0::Int)..10] (\counter -> do
[stopped] <- takeMVar mv
assert "breakpoint number" ((S.bkptHitNumber . S.stoppedReason) stopped) (S.bkptNumber bp)
value' <- cmd R.RCDone $ C.data_evaluate_expression "i"
let (Just value) = S.response_data_evaluate_expression value'
assert "value of i" value (show counter)
_ <- cmd R.RCRunning $ C.exec_continue False (Left True)
return ()
)
G.shutdown ctx
readFile "gdb.log" >>= putStr
withTemporaryDirectory :: IO a -> IO a
withTemporaryDirectory f = bracket acquire release inbetween
where
acquire = do
tmpdir <- createTempDirectory "/tmp" "hgdbmi-test"
curdir <- getCurrentDirectory
setCurrentDirectory tmpdir
return (curdir, tmpdir)
release (curdir, tmpdir) = do
setCurrentDirectory curdir
removeDirectoryRecursive tmpdir
inbetween (_, _) = f
main :: IO ()
main = withTemporaryDirectory test