sandwich-0.3.0.0: src/Test/Sandwich/Formatters/FailureReport.hs
{-# LANGUAGE MultiWayIf #-}
-- | The failure report formatter is like the print formatter, but it only shows failures.
--
-- Documentation can be found <https://codedownio.github.io/sandwich/docs/formatters/failure_report here>.
module Test.Sandwich.Formatters.FailureReport (
defaultFailureReportFormatter
, FailureReportFormatter
-- * Options
, failureReportUseColor
, failureReportLogLevel
, failureReportIncludeCallStacks
, failureReportIndentSize
, failureReportVisibilityThreshold
) where
import Control.Monad
import Control.Monad.IO.Class
import Control.Monad.Logger
import Control.Monad.Reader
import Data.Foldable
import Data.Function
import qualified Data.Map as M
import Data.Maybe
import qualified Data.Sequence as Seq
import Data.String.Interpolate
import qualified Data.Text as T
import System.IO
import Test.Sandwich.Formatters.Print.Common
import Test.Sandwich.Formatters.Print.FailureReason
import Test.Sandwich.Formatters.Print.Printing
import Test.Sandwich.Formatters.Print.Types
import Test.Sandwich.Formatters.Print.Util
import Test.Sandwich.Interpreters.RunTree.Util
import Test.Sandwich.RunTree
import Test.Sandwich.Types.RunTree
import Test.Sandwich.Types.Spec
data FailureReportFormatter = FailureReportFormatter {
failureReportUseColor :: Bool
, failureReportLogLevel :: Maybe LogLevel
, failureReportIncludeCallStacks :: Bool
, failureReportIndentSize :: Int
, failureReportVisibilityThreshold :: Int
, failureReportIncludeTimestamps :: IncludeTimestamps
} deriving (Show)
defaultFailureReportFormatter :: FailureReportFormatter
defaultFailureReportFormatter = FailureReportFormatter {
failureReportUseColor = True
, failureReportLogLevel = Just LevelWarn
, failureReportIncludeCallStacks = True
, failureReportIndentSize = 4
, failureReportVisibilityThreshold = 50
, failureReportIncludeTimestamps = IncludeTimestampsNever
}
instance Formatter FailureReportFormatter where
formatterName _ = "failure-report-formatter"
runFormatter _ _ _ _ = return ()
finalizeFormatter = printFailureReport
printFailureReport :: (MonadIO m) => FailureReportFormatter -> [RunNode BaseContext] -> BaseContext -> m ()
printFailureReport frf@(FailureReportFormatter {..}) rts _bc = do
liftIO $ putStrLn [i|\n\nFailure report:|]
let pf = PrintFormatter {
printFormatterUseColor = failureReportUseColor
, printFormatterLogLevel = failureReportLogLevel
, printFormatterVisibilityThreshold = maxBound
, printFormatterIncludeCallStacks = failureReportIncludeCallStacks
, printFormatterIndentSize = failureReportIndentSize
, printFormatterIncludeTimestamps = failureReportIncludeTimestamps
}
let extractFromNode node = let RunNodeCommonWithStatus {..} = runNodeCommon node in (runTreeId, (T.pack runTreeLabel, runTreeVisibilityLevel))
let idToLabel = M.fromList $ mconcat [extractValues extractFromNode node | node <- rts]
liftIO $ runReaderT (mapM_ (runWithIndentation frf idToLabel) rts) (pf, 0, stdout)
runWithIndentation :: FailureReportFormatter -> M.Map Int (T.Text, Int) -> RunNode context -> ReaderT (PrintFormatter, Int, Handle) IO ()
runWithIndentation frf@(FailureReportFormatter {..}) idToLabel node = do
let common@(RunNodeCommonWithStatus {..}) = runNodeCommon node
case node of
RunNodeIt {} -> return ()
RunNodeIntroduce {..} -> forM_ runNodeChildrenAugmented (runWithIndentation frf idToLabel)
RunNodeIntroduceWith {..} -> forM_ runNodeChildrenAugmented (runWithIndentation frf idToLabel)
_ -> forM_ (runNodeChildren node) (runWithIndentation frf idToLabel)
result <- liftIO $ waitForTree node
-- Print the failure reason
case result of
Success -> return ()
DryRun -> return ()
Cancelled -> return ()
Failure (ChildrenFailed {}) -> return ()
Failure reason -> do
p "\n"
let ancestorIds = runTreeAncestors
let ancestorNames = fmap (\k -> fromMaybe ("?", 0) $ M.lookup k idToLabel) ancestorIds
& Seq.filter (\(_, visibilityLevel) -> visibilityLevel <= failureReportVisibilityThreshold)
& fmap fst
let label = T.unpack $ T.intercalate ", " (toList ancestorNames)
case reason of
Pending {} -> do
pYellowLn label
_ -> do
-- TODO: get full list of ancestor labels joined on ", "
pRedLn label
withBumpIndent $ printFailureReason reason
finishPrinting common result