module Util ( doctestTestCase , Util.cases , errors , failures ) where import System.Directory (getCurrentDirectory, setCurrentDirectory) import System.Exit (ExitCode(ExitSuccess)) import System.Process (readProcessWithExitCode) import qualified Test.HUnit as HU import Test.HUnit (assertEqual, Counts(..), Test(TestCase), Assertion, showCounts) import Data.Char (isSpace) cases :: Int -> Counts cases n = Counts { HU.cases = n , tried = n , errors = 0 , failures = 0 } -- | Construct a doctest specific 'TestCase'. doctestTestCase :: FilePath -- ^ absolute path to `doctest` binary -> FilePath -- ^ current directory of forked `doctest` process -> [String] -- ^ args, given to doctest -> Counts -- ^ expected test result -> Test doctestTestCase doctest dir args counts = TestCase $ doctestAssert doctest dir args counts -- | Construct a doctest specific 'Assertion'. doctestAssert :: FilePath -- ^ absolute path to `doctest` binary -> FilePath -- ^ current directory of forked `doctest` process -> [String] -- ^ args, given to `doctest` -> Counts -- ^ expected test result -> Assertion doctestAssert doctest workingDir args counts = do out <- runDoctest doctest workingDir args assertEqual label (showCounts counts) (last . lines $ out) where label = workingDir ++ " " ++ show args -- | Fork and run a `doctest` process. runDoctest :: FilePath -- ^ absolute path to `doctest` binary -> FilePath -- ^ current directory of forked `doctest` process -> [String] -- ^ args, given to `doctest` -> IO String -- ^ final result, as printed by `doctest` runDoctest doctest workingDir args = do cwd <- getCurrentDirectory setCurrentDirectory workingDir (exit, out, err) <- readProcessWithExitCode doctest args "" setCurrentDirectory cwd case exit of ExitSuccess -> return $ lastLine err _ -> error $ mayCat "STDERR:" (strip err) ++ mayCat "STDOUT:" (strip out) where mayCat x y = if null y then "" else unlines ["", x, y] where lastLine = reverse . takeWhile (/= '\r') . reverse -- | Remove leading and trailing whitespace from given string. -- -- Example: -- -- >>> strip " \tfoo\nbar \t\n " -- "foo\nbar" strip :: String -> String strip = dropWhile isSpace . reverse . dropWhile isSpace . reverse