{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE OverloadedStrings #-} import Options.Applicative.Simple hiding(action) import GHC.IO.Handle import System.IO import System.Environment import Control.Exception import Control.Monad import System.Directory import System.Exit import Data.ByteString (ByteString) import qualified Data.ByteString as BS import Data.Monoid ((<>)) shouldBe :: (Show a, Eq a) => a -> a -> IO () shouldBe actual expected | expected == actual = return () | otherwise = do putStrLn $ "expected: " ++ show expected putStrLn $ "actual : " ++ show actual exitFailure catchReturn :: Exception e => IO e -> IO e catchReturn io = io `catch` return catchExitCode :: IO () -> IO ExitCode catchExitCode action = catchReturn $ do action return ExitSuccess data FakeHandles = FakeHandles { fakeIn :: Handle , fakeOut :: Handle , fakeErr :: Handle , realIn :: Handle , realOut :: Handle , realErr :: Handle } openFile' :: FilePath -> IO Handle openFile' path = do removeIfExists path openFile path ReadWriteMode removeIfExists :: FilePath -> IO () removeIfExists path = do exists <- doesFileExist path when exists $ do removeFile path stdinFile :: FilePath stdinFile = ".tmp.stdin" stdoutFile :: FilePath stdoutFile = ".tmp.stdout" stderrFile :: FilePath stderrFile = ".tmp.stderr" beforeFH :: IO FakeHandles beforeFH = do realIn <- hDuplicate stdin realOut <- hDuplicate stdout realErr <- hDuplicate stderr fakeIn <- openFile stdinFile ReadWriteMode fakeOut <- openFile' stdoutFile fakeErr <- openFile' stderrFile hDuplicateTo fakeIn stdin hDuplicateTo fakeOut stdout hDuplicateTo fakeErr stderr return FakeHandles{..} afterFH :: FakeHandles -> IO () afterFH FakeHandles{..} = do hDuplicateTo realIn stdin hDuplicateTo realOut stdout hDuplicateTo realErr stderr hClose fakeIn hClose fakeOut hClose fakeErr withFakeHandles :: IO a -> IO a withFakeHandles = bracket beforeFH afterFH . const withStdIn :: ByteString -> IO () -> IO (ByteString, ByteString, ExitCode) withStdIn inBS action = do BS.writeFile stdinFile inBS withFakeHandles $ do _ <- catchExitCode action hFlush stdout hFlush stderr out <- BS.readFile stdoutFile err <- BS.readFile stderrFile removeIfExists stdinFile removeIfExists stdoutFile removeIfExists stderrFile return (out, err, ExitSuccess) main :: IO () main = do (out, err, exitCode) <- withStdIn "" $ withArgs ["--version"] $ simpleProg exitCode `shouldBe` ExitSuccess err `shouldBe` "" out `shouldBe` "version\n" (out', err', exitCode') <- withStdIn "" $ withArgs ["--summary"] $ summaryProg exitCode' `shouldBe` ExitSuccess err' `shouldBe` "" out' `shouldBe` "A program summary\n" return () simpleProg :: IO () simpleProg = do ((), ()) <- simpleOptions "version" "header" "desc" (pure ()) empty return () parserWithSummary :: Parser () parserWithSummary = summaryOption <*> pure () where summaryOption = infoOption "A program summary" $ long "summary" <> help "Show program summary" summaryProg :: IO () summaryProg = do ((), ()) <- simpleOptions "version" "header" "desc" parserWithSummary empty return ()