{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE ExistentialQuantification #-}
module Test.Hspec.Core.Formatters.Monad (
Formatter (..)
, FailureReason (..)
, FormatM
, getSuccessCount
, getPendingCount
, getFailCount
, getTotalCount
, FailureRecord (..)
, getFailMessages
, usedSeed
, getCPUTime
, getRealTime
, write
, writeLine
, writeTransient
, withInfoColor
, withSuccessColor
, withPendingColor
, withFailColor
, useDiff
, extraChunk
, missingChunk
, Environment(..)
, interpretWith
) where
import Prelude ()
import Test.Hspec.Core.Compat
import Control.Monad.IO.Class
import Test.Hspec.Core.Formatters.Free
import Test.Hspec.Core.Example (FailureReason(..))
import Test.Hspec.Core.Util (Path)
import Test.Hspec.Core.Spec (Progress, Location)
import Test.Hspec.Core.Clock
data Formatter = Formatter {
headerFormatter :: FormatM ()
, exampleGroupStarted :: [String] -> String -> FormatM ()
, exampleGroupDone :: FormatM ()
, exampleProgress :: Path -> Progress -> FormatM ()
, exampleSucceeded :: Path -> String -> FormatM ()
, exampleFailed :: Path -> String -> FailureReason -> FormatM ()
, examplePending :: Path -> String -> Maybe String -> FormatM ()
, failedFormatter :: FormatM ()
, footerFormatter :: FormatM ()
}
data FailureRecord = FailureRecord {
failureRecordLocation :: Maybe Location
, failureRecordPath :: Path
, failureRecordMessage :: FailureReason
}
data FormatF next =
GetSuccessCount (Int -> next)
| GetPendingCount (Int -> next)
| GetFailMessages ([FailureRecord] -> next)
| UsedSeed (Integer -> next)
| GetCPUTime (Maybe Seconds -> next)
| GetRealTime (Seconds -> next)
| Write String next
| WriteTransient String next
| forall a. WithFailColor (FormatM a) (a -> next)
| forall a. WithSuccessColor (FormatM a) (a -> next)
| forall a. WithPendingColor (FormatM a) (a -> next)
| forall a. WithInfoColor (FormatM a) (a -> next)
| UseDiff (Bool -> next)
| ExtraChunk String next
| MissingChunk String next
| forall a. LiftIO (IO a) (a -> next)
instance Functor FormatF where
fmap f x = case x of
GetSuccessCount next -> GetSuccessCount (fmap f next)
GetPendingCount next -> GetPendingCount (fmap f next)
GetFailMessages next -> GetFailMessages (fmap f next)
UsedSeed next -> UsedSeed (fmap f next)
GetCPUTime next -> GetCPUTime (fmap f next)
GetRealTime next -> GetRealTime (fmap f next)
Write s next -> Write s (f next)
WriteTransient s next -> WriteTransient s (f next)
WithFailColor action next -> WithFailColor action (fmap f next)
WithSuccessColor action next -> WithSuccessColor action (fmap f next)
WithPendingColor action next -> WithPendingColor action (fmap f next)
WithInfoColor action next -> WithInfoColor action (fmap f next)
UseDiff next -> UseDiff (fmap f next)
ExtraChunk s next -> ExtraChunk s (f next)
MissingChunk s next -> MissingChunk s (f next)
LiftIO action next -> LiftIO action (fmap f next)
type FormatM = Free FormatF
instance MonadIO FormatM where
liftIO s = liftF (LiftIO s id)
data Environment m = Environment {
environmentGetSuccessCount :: m Int
, environmentGetPendingCount :: m Int
, environmentGetFailMessages :: m [FailureRecord]
, environmentUsedSeed :: m Integer
, environmentGetCPUTime :: m (Maybe Seconds)
, environmentGetRealTime :: m Seconds
, environmentWrite :: String -> m ()
, environmentWriteTransient :: String -> m ()
, environmentWithFailColor :: forall a. m a -> m a
, environmentWithSuccessColor :: forall a. m a -> m a
, environmentWithPendingColor :: forall a. m a -> m a
, environmentWithInfoColor :: forall a. m a -> m a
, environmentUseDiff :: m Bool
, environmentExtraChunk :: String -> m ()
, environmentMissingChunk :: String -> m ()
, environmentLiftIO :: forall a. IO a -> m a
}
interpretWith :: forall m a. Monad m => Environment m -> FormatM a -> m a
interpretWith Environment{..} = go
where
go :: forall b. FormatM b -> m b
go m = case m of
Pure value -> return value
Free action -> case action of
GetSuccessCount next -> environmentGetSuccessCount >>= go . next
GetPendingCount next -> environmentGetPendingCount >>= go . next
GetFailMessages next -> environmentGetFailMessages >>= go . next
UsedSeed next -> environmentUsedSeed >>= go . next
GetCPUTime next -> environmentGetCPUTime >>= go . next
GetRealTime next -> environmentGetRealTime >>= go . next
Write s next -> environmentWrite s >> go next
WriteTransient s next -> environmentWriteTransient s >> go next
WithFailColor inner next -> environmentWithFailColor (go inner) >>= go . next
WithSuccessColor inner next -> environmentWithSuccessColor (go inner) >>= go . next
WithPendingColor inner next -> environmentWithPendingColor (go inner) >>= go . next
WithInfoColor inner next -> environmentWithInfoColor (go inner) >>= go . next
UseDiff next -> environmentUseDiff >>= go . next
ExtraChunk s next -> environmentExtraChunk s >> go next
MissingChunk s next -> environmentMissingChunk s >> go next
LiftIO inner next -> environmentLiftIO inner >>= go . next
getSuccessCount :: FormatM Int
getSuccessCount = liftF (GetSuccessCount id)
getPendingCount :: FormatM Int
getPendingCount = liftF (GetPendingCount id)
getFailCount :: FormatM Int
getFailCount = length <$> getFailMessages
getTotalCount :: FormatM Int
getTotalCount = sum <$> sequence [getSuccessCount, getFailCount, getPendingCount]
getFailMessages :: FormatM [FailureRecord]
getFailMessages = liftF (GetFailMessages id)
usedSeed :: FormatM Integer
usedSeed = liftF (UsedSeed id)
getCPUTime :: FormatM (Maybe Seconds)
getCPUTime = liftF (GetCPUTime id)
getRealTime :: FormatM Seconds
getRealTime = liftF (GetRealTime id)
write :: String -> FormatM ()
write s = liftF (Write s ())
writeLine :: String -> FormatM ()
writeLine s = write s >> write "\n"
writeTransient :: String -> FormatM ()
writeTransient s = liftF (WriteTransient s ())
withFailColor :: FormatM a -> FormatM a
withFailColor s = liftF (WithFailColor s id)
withSuccessColor :: FormatM a -> FormatM a
withSuccessColor s = liftF (WithSuccessColor s id)
withPendingColor :: FormatM a -> FormatM a
withPendingColor s = liftF (WithPendingColor s id)
withInfoColor :: FormatM a -> FormatM a
withInfoColor s = liftF (WithInfoColor s id)
useDiff :: FormatM Bool
useDiff = liftF (UseDiff id)
extraChunk :: String -> FormatM ()
extraChunk s = liftF (ExtraChunk s ())
missingChunk :: String -> FormatM ()
missingChunk s = liftF (MissingChunk s ())