{-# LANGUAGE CPP #-}
module Test.Hspec.Core.Formatters (
silent
, specdoc
, progress
, failed_examples
, Formatter (..)
, FailureReason (..)
, FormatM
, getSuccessCount
, getPendingCount
, getFailCount
, getTotalCount
, FailureRecord (..)
, getFailMessages
, usedSeed
, Seconds(..)
, getCPUTime
, getRealTime
, write
, writeLine
, writeTransient
, withInfoColor
, withSuccessColor
, withPendingColor
, withFailColor
, extraChunk
, missingChunk
, formatException
) where
import Prelude ()
import Test.Hspec.Core.Compat hiding (First)
import Data.Maybe
import Test.Hspec.Core.Util
import Test.Hspec.Core.Spec (Location(..))
import Text.Printf
import Control.Monad (unless)
import Test.Hspec.Core.Formatters.Monad (
Formatter (..)
, FailureReason (..)
, FormatM
, getSuccessCount
, getPendingCount
, getFailCount
, getTotalCount
, FailureRecord (..)
, getFailMessages
, usedSeed
, getCPUTime
, getRealTime
, write
, writeLine
, writeTransient
, withInfoColor
, withSuccessColor
, withPendingColor
, withFailColor
, extraChunk
, missingChunk
)
import Test.Hspec.Core.Clock (Seconds(..))
import Test.Hspec.Core.Formatters.Diff
silent :: Formatter
silent = Formatter {
headerFormatter = return ()
, exampleGroupStarted = \_ _ -> return ()
, exampleGroupDone = return ()
, exampleProgress = \_ _ -> return ()
, exampleSucceeded = \ _ _ -> return ()
, exampleFailed = \_ _ _ -> return ()
, examplePending = \_ _ _ -> return ()
, failedFormatter = return ()
, footerFormatter = return ()
}
specdoc :: Formatter
specdoc = silent {
headerFormatter = do
writeLine ""
, exampleGroupStarted = \nesting name -> do
writeLine (indentationFor nesting ++ name)
, exampleProgress = \_ p -> do
writeTransient (formatProgress p)
, exampleSucceeded = \(nesting, requirement) info -> withSuccessColor $ do
writeLine $ indentationFor nesting ++ requirement
forM_ (lines info) $ \ s ->
writeLine $ indentationFor ("" : nesting) ++ s
, exampleFailed = \(nesting, requirement) info _ -> withFailColor $ do
n <- getFailCount
writeLine $ indentationFor nesting ++ requirement ++ " FAILED [" ++ show n ++ "]"
forM_ (lines info) $ \ s ->
writeLine $ indentationFor ("" : nesting) ++ s
, examplePending = \(nesting, requirement) info reason -> withPendingColor $ do
writeLine $ indentationFor nesting ++ requirement
forM_ (lines info) $ \ s ->
writeLine $ indentationFor ("" : nesting) ++ s
writeLine $ indentationFor ("" : nesting) ++ "# PENDING: " ++ fromMaybe "No reason given" reason
, failedFormatter = defaultFailedFormatter
, footerFormatter = defaultFooter
} where
indentationFor nesting = replicate (length nesting * 2) ' '
formatProgress (current, total)
| total == 0 = show current
| otherwise = show current ++ "/" ++ show total
progress :: Formatter
progress = silent {
exampleSucceeded = \_ _ -> withSuccessColor $ write "."
, exampleFailed = \_ _ _ -> withFailColor $ write "F"
, examplePending = \_ _ _ -> withPendingColor $ write "."
, failedFormatter = defaultFailedFormatter
, footerFormatter = defaultFooter
}
failed_examples :: Formatter
failed_examples = silent {
failedFormatter = defaultFailedFormatter
, footerFormatter = defaultFooter
}
defaultFailedFormatter :: FormatM ()
defaultFailedFormatter = do
writeLine ""
failures <- getFailMessages
unless (null failures) $ do
writeLine "Failures:"
writeLine ""
forM_ (zip [1..] failures) $ \x -> do
formatFailure x
writeLine ""
#if __GLASGOW_HASKELL__ == 800
withFailColor $ do
writeLine "WARNING:"
writeLine " Your version of GHC is affected by https://ghc.haskell.org/trac/ghc/ticket/13285."
writeLine " Source locations may not work as expected."
writeLine ""
writeLine " Please consider upgrading GHC!"
writeLine ""
#endif
write "Randomized with seed " >> usedSeed >>= writeLine . show
writeLine ""
where
formatFailure :: (Int, FailureRecord) -> FormatM ()
formatFailure (n, FailureRecord mLoc path reason) = do
forM_ mLoc $ \loc -> do
withInfoColor $ writeLine (formatLoc loc)
write (" " ++ show n ++ ") ")
writeLine (formatRequirement path)
case reason of
NoReason -> return ()
Reason err -> withFailColor $ indent err
ExpectedButGot preface expected actual -> do
mapM_ indent preface
let chunks = diff expected actual
withFailColor $ write (indentation ++ "expected: ")
forM_ chunks $ \chunk -> case chunk of
Both a _ -> indented write a
First a -> indented extraChunk a
Second _ -> return ()
writeLine ""
withFailColor $ write (indentation ++ " but got: ")
forM_ chunks $ \chunk -> case chunk of
Both a _ -> indented write a
First _ -> return ()
Second a -> indented missingChunk a
writeLine ""
where
indented output text = case break (== '\n') text of
(xs, "") -> output xs
(xs, _ : ys) -> output (xs ++ "\n") >> write (indentation ++ " ") >> indented output ys
Error _ e -> withFailColor . indent $ (("uncaught exception: " ++) . formatException) e
where
indentation = " "
indent message = do
forM_ (lines message) $ \line -> do
writeLine (indentation ++ line)
formatLoc (Location file line column) = " " ++ file ++ ":" ++ show line ++ ":" ++ show column ++ ": "
defaultFooter :: FormatM ()
defaultFooter = do
writeLine =<< (++)
<$> (printf "Finished in %1.4f seconds" <$> getRealTime)
<*> (maybe "" (printf ", used %1.4f seconds of CPU time") <$> getCPUTime)
fails <- getFailCount
pending <- getPendingCount
total <- getTotalCount
let
output =
pluralize total "example"
++ ", " ++ pluralize fails "failure"
++ if pending == 0 then "" else ", " ++ show pending ++ " pending"
c | fails /= 0 = withFailColor
| pending /= 0 = withPendingColor
| otherwise = withSuccessColor
c $ writeLine output