module Runner (
runModules
, Summary(..)
#ifdef TEST
, Report
, ReportState (..)
, report
, report_
#endif
) where
import Prelude hiding (putStr, putStrLn, error)
#if __GLASGOW_HASKELL__ < 710
import Data.Monoid
import Control.Applicative
#endif
import Control.Monad hiding (forM_)
import Text.Printf (printf)
import System.IO (hPutStrLn, hPutStr, stderr, hIsTerminalDevice)
import Data.Foldable (forM_)
import Control.Monad.Trans.State
import Control.Monad.IO.Class
import Interpreter (Interpreter)
import qualified Interpreter
import Parse
import Location
import Property
import Runner.Example
data Summary = Summary {
sExamples :: Int
, sTried :: Int
, sErrors :: Int
, sFailures :: Int
} deriving Eq
instance Show Summary where
show (Summary examples tried errors failures) =
printf "Examples: %d Tried: %d Errors: %d Failures: %d" examples tried errors failures
instance Monoid Summary where
mempty = Summary 0 0 0 0
(Summary x1 x2 x3 x4) `mappend` (Summary y1 y2 y3 y4) = Summary (x1 + y1) (x2 + y2) (x3 + y3) (x4 + y4)
runModules :: Interpreter -> [Module [Located DocTest]] -> IO Summary
runModules repl modules = do
isInteractive <- hIsTerminalDevice stderr
ReportState _ _ s <- (`execStateT` ReportState 0 isInteractive mempty {sExamples = c}) $ do
forM_ modules $ runModule repl
gets (show . reportStateSummary) >>= report
return s
where
c = (sum . map count) modules
count :: Module [Located DocTest] -> Int
count (Module _ setup tests) = sum (map length tests) + maybe 0 length setup
type Report = StateT ReportState IO
data ReportState = ReportState {
reportStateCount :: Int
, reportStateInteractive :: Bool
, reportStateSummary :: Summary
}
report :: String -> Report ()
report msg = do
overwrite msg
liftIO $ hPutStrLn stderr ""
modify (\st -> st {reportStateCount = 0})
report_ :: String -> Report ()
report_ msg = do
f <- gets reportStateInteractive
when f $ do
overwrite msg
modify (\st -> st {reportStateCount = length msg})
overwrite :: String -> Report ()
overwrite msg = do
n <- gets reportStateCount
let str | 0 < n = "\r" ++ msg ++ replicate (n length msg) ' '
| otherwise = msg
liftIO (hPutStr stderr str)
runModule :: Interpreter -> Module [Located DocTest] -> Report ()
runModule repl (Module module_ setup examples) = do
Summary _ _ e0 f0 <- gets reportStateSummary
forM_ setup $
runTestGroup repl reload
Summary _ _ e1 f1 <- gets reportStateSummary
when (e0 == e1 && f0 == f1) $
forM_ examples $
runTestGroup repl setup_
where
reload :: IO ()
reload = do
void $ Interpreter.safeEval repl ":reload"
void $ Interpreter.safeEval repl $ ":m *" ++ module_
setup_ :: IO ()
setup_ = do
reload
forM_ setup $ \l -> forM_ l $ \(Located _ x) -> case x of
Property _ -> return ()
Example e _ -> void $ Interpreter.safeEval repl e
reportFailure :: Location -> Expression -> Report ()
reportFailure loc expression = do
report (printf "### Failure in %s: expression `%s'" (show loc) expression)
updateSummary (Summary 0 1 0 1)
reportError :: Location -> Expression -> String -> Report ()
reportError loc expression err = do
report (printf "### Error in %s: expression `%s'" (show loc) expression)
report err
updateSummary (Summary 0 1 1 0)
reportSuccess :: Report ()
reportSuccess =
updateSummary (Summary 0 1 0 0)
updateSummary :: Summary -> Report ()
updateSummary summary = do
ReportState n f s <- get
put (ReportState n f $ s `mappend` summary)
runTestGroup :: Interpreter -> IO () -> [Located DocTest] -> Report ()
runTestGroup repl setup tests = do
gets (show . reportStateSummary) >>= report_
liftIO setup
runExampleGroup repl examples
forM_ properties $ \(loc, expression) -> do
r <- liftIO $ do
setup
runProperty repl expression
case r of
Success ->
reportSuccess
Error err -> do
reportError loc expression err
Failure msg -> do
reportFailure loc expression
report msg
where
properties = [(loc, p) | Located loc (Property p) <- tests]
examples :: [Located Interaction]
examples = [Located loc (e, r) | Located loc (Example e r) <- tests]
runExampleGroup :: Interpreter -> [Located Interaction] -> Report ()
runExampleGroup repl = go
where
go ((Located loc (expression, expected)) : xs) = do
r <- fmap lines <$> liftIO (Interpreter.safeEval repl expression)
case r of
Left err -> do
reportError loc expression err
Right actual -> case mkResult expected actual of
NotEqual err -> do
reportFailure loc expression
mapM_ report err
Equal -> do
reportSuccess
go xs
go [] = return ()