shelltestrunner 0.4 → 0.5
raw patch · 2 files changed
+35/−37 lines, 2 files
Files
- shelltestrunner.cabal +11/−14
- shelltestrunner.hs +24/−23
shelltestrunner.cabal view
@@ -1,10 +1,10 @@ name: shelltestrunner-version: 0.4+version: 0.5 category: Testing synopsis: A tool for testing command-line programs. description: .- Run a given program through "shell" tests specifed by one or more test+ Run a given program through \"shell\" tests specifed by one or more test files, each of which can specify: command-line arguments, input, expected output, expected stderr output, and expected exit code. This was extracted from the hledger project, inspired by the tests in John@@ -31,27 +31,24 @@ > >>>= [/regexp/] > [..or expected numeric exit code] .- The expected fields can also have a regular expression match expression- following the delimiter on the same line, with no other data lines, in- which case the test passes if the output is matched by the regexp. The- regexp is enclosed in forward slashess. A ! preceding the expression- negates the match. For example, to check that stdout does not contain- "axe":- .- > >>> !/axe/+ Each expected field can have either a regular expression match+ expression, in which case the test passes if the output is matched, or 0+ or more data lines, in which case the output must match these exactly. A+ ! preceding a /regexp/ negates the match. The regular expression syntax+ is that supported by the regexpr library. . Apart from the command line, all fields are optional. Only fields specified in the test will be tested, unless you use the -i/--implicit-tests flag, which will test for empty stdout, empty stderr,- or 0 exit code whenever fields are omitted.+ or 0 exit code where fields are omitted. . Issues: .- - output order is mixed up- .- - option processing is weak+ - can't put / in a regexp . - can't test input/output which does not end with newline+ .+ - option processing is weak . Wishlist: .
shelltestrunner.hs view
@@ -14,13 +14,13 @@ import Control.Concurrent (forkIO) import Control.Concurrent.MVar (newEmptyMVar, putMVar, takeMVar) import Control.Monad (liftM,when)-import Data.List (partition)+import Data.List (partition, intercalate) import Data.Maybe (fromJust,isJust,maybe) import qualified Test.HUnit (Test) import System.Console.ParseArgs hiding (args) import System.Environment (withArgs) import System.Exit (ExitCode(..),exitWith)-import System.IO (Handle, hGetContents, hPutStr, stderr)+import System.IO (Handle, hGetContents, hPutStr) import System.Process (runInteractiveCommand, waitForProcess) import Test.Framework (defaultMain) import Test.Framework.Providers.HUnit (hUnitTestToTests)@@ -34,7 +34,7 @@ version :: String-version = "0.4" -- keep synced with .cabal+version = "0.5" -- keep synced with .cabal data ArgId = HelpFlag | VersionFlag@@ -201,11 +201,9 @@ -- running shellTestToHUnitTest :: Args ArgId -> ShellTest -> Test.HUnit.Test-shellTestToHUnitTest args t = testname t ~: do {r <- runShellTest args t; assertBool "" r}--runShellTest :: Args ArgId -> ShellTest -> IO Bool-runShellTest args ShellTest{testname=n,commandargs=c,stdin=i,stdoutExpected=o_expected,- stderrExpected=e_expected,exitCodeExpected=x_expected} = do+shellTestToHUnitTest args ShellTest{testname=n,commandargs=c,stdin=i,stdoutExpected=o_expected,+ stderrExpected=e_expected,exitCodeExpected=x_expected} = + n ~: do let exe = fromJust $ getArgString args ExecutableArg cmd = unwords [exe,c] (o_expected',e_expected',x_expected') =@@ -234,17 +232,19 @@ o_actual <- takeMVar o e_actual <- takeMVar e - let o_ok = maybe True (o_actual `matches`) o_expected'- let e_ok = maybe True (e_actual `matches`) e_expected'- let x_ok = maybe True ((show $ fromExitCode x_actual) `matches`) x_expected'- if o_ok && e_ok && x_ok- then do- return True - else do- when (not o_ok) $ printExpectedActual "stdout" (fromJust o_expected') o_actual- when (not e_ok) $ printExpectedActual "stderr" (fromJust e_expected') e_actual- when (not x_ok) $ printExpectedActual "exit code" (fromJust x_expected') (show $ fromExitCode x_actual)- return False+ assertString $ addnewline $ intercalate "\n" $ filter (not . null)+ [if (maybe True (o_actual `matches`) o_expected')+ then "" + else showExpectedActual "stdout" (fromJust o_expected') o_actual+ ,if (maybe True (e_actual `matches`) e_expected')+ then "" + else showExpectedActual "stderr" (fromJust e_expected') e_actual+ ,if (maybe True ((show $ fromExitCode x_actual) `matches`) x_expected')+ then ""+ else showExpectedActual "exit code" (fromJust x_expected') (show $ fromExitCode x_actual)+ ]+ where addnewline "" = ""+ addnewline s = "\n"++s hGetContentsStrictlyAnd :: Handle -> (String -> IO b) -> IO b hGetContentsStrictlyAnd h f = hGetContents h >>= \c -> length c `seq` f c@@ -255,14 +255,15 @@ matches s (Numeric p) = s == p matches s (Exact p) = s == p +showExpectedActual :: String -> Matcher -> String -> String+showExpectedActual field e a =+ printf "**Expected %s:%s**Got %s:\n%s" field (showMatcher e) field a+ showMatcher :: Matcher -> String showMatcher (PositiveRegex r) = " /"++r++"/\n" showMatcher (NegativeRegex r) = " !/"++r++"/\n"-showMatcher (Numeric s) = "\n"++s+showMatcher (Numeric s) = "\n"++s++"\n" showMatcher (Exact s) = "\n"++s--printExpectedActual :: String -> Matcher -> String -> IO ()-printExpectedActual f e a = hPutStr stderr $ printf "**Expected %s:%s**Got %s:\n%s" f (showMatcher e) f a -- utils