{-# LANGUAGE BangPatterns, ImplicitParams, MultiParamTypeClasses, DeriveDataTypeable, FlexibleContexts #-}
module Test.Tasty.Ingredients.ConsoleReporter
( consoleTestReporter
, consoleTestReporterWithHook
, Quiet(..)
, HideSuccesses(..)
, AnsiTricks(..)
, UseColor(..)
, useColor
, Statistics(..)
, computeStatistics
, printStatistics
, printStatisticsNoTime
, TestOutput(..)
, buildTestOutput
, foldTestOutput
, withConsoleFormat
) where
import Prelude hiding (fail, EQ)
import Control.Monad (join, unless, void, when, (<=<))
import Control.Monad.IO.Class (liftIO)
import Control.Monad.Trans.Reader (Reader, runReader, ask)
import Control.Monad.Trans.State (evalState, evalStateT, get, modify, put)
import Control.Concurrent.STM
import Control.Exception
import Test.Tasty.Core
import Test.Tasty.Providers.ConsoleFormat
import Test.Tasty.Run
import Test.Tasty.Ingredients
import Test.Tasty.Ingredients.ListTests
import Test.Tasty.Options
import Test.Tasty.Options.Core
import Test.Tasty.Patterns
import Test.Tasty.Patterns.Printer
import Test.Tasty.Patterns.Types
import Test.Tasty.Runners.Reducers
import Test.Tasty.Runners.Utils
import Text.Printf
import qualified Data.IntMap as IntMap
import Data.Char
#ifdef VERSION_wcwidth
import Data.Char.WCWidth (wcwidth)
#endif
import Data.List (isInfixOf)
import Data.Maybe
import Data.Monoid (Any(..))
import qualified Data.Semigroup as Sem
import Data.Typeable
import Options.Applicative hiding (action, str, Success, Failure)
import System.IO
import System.Console.ANSI
#if !MIN_VERSION_base(4,11,0)
import Data.Foldable (foldMap)
#endif
data TestOutput
= PrintTest
String
(IO ())
(Result -> IO ())
| PrintHeading String (IO ()) TestOutput
| Skip
| Seq TestOutput TestOutput
instance Sem.Semigroup TestOutput where
<> :: TestOutput -> TestOutput -> TestOutput
(<>) = TestOutput -> TestOutput -> TestOutput
Seq
instance Monoid TestOutput where
mempty :: TestOutput
mempty = TestOutput
Skip
#if !MIN_VERSION_base(4,11,0)
mappend = (Sem.<>)
#endif
applyHook :: ([TestName] -> Result -> IO Result) -> TestOutput -> TestOutput
applyHook :: ([TestName] -> Result -> IO Result) -> TestOutput -> TestOutput
applyHook [TestName] -> Result -> IO Result
hook = [TestName] -> TestOutput -> TestOutput
go []
where
go :: [TestName] -> TestOutput -> TestOutput
go [TestName]
path (PrintTest TestName
name IO ()
printName Result -> IO ()
printResult) =
TestName -> IO () -> (Result -> IO ()) -> TestOutput
PrintTest TestName
name IO ()
printName (Result -> IO ()
printResult forall (m :: * -> *) b c a.
Monad m =>
(b -> m c) -> (a -> m b) -> a -> m c
<=< [TestName] -> Result -> IO Result
hook (TestName
name forall a. a -> [a] -> [a]
: [TestName]
path))
go [TestName]
path (PrintHeading TestName
name IO ()
printName TestOutput
printBody) =
TestName -> IO () -> TestOutput -> TestOutput
PrintHeading TestName
name IO ()
printName ([TestName] -> TestOutput -> TestOutput
go (TestName
name forall a. a -> [a] -> [a]
: [TestName]
path) TestOutput
printBody)
go [TestName]
path (Seq TestOutput
a TestOutput
b) = TestOutput -> TestOutput -> TestOutput
Seq ([TestName] -> TestOutput -> TestOutput
go [TestName]
path TestOutput
a) ([TestName] -> TestOutput -> TestOutput
go [TestName]
path TestOutput
b)
go [TestName]
_ TestOutput
Skip = forall a. Monoid a => a
mempty
type Level = Int
buildTestOutput :: (?colors :: Bool) => OptionSet -> TestTree -> TestOutput
buildTestOutput :: (?colors::Bool) => OptionSet -> TestTree -> TestOutput
buildTestOutput OptionSet
opts TestTree
tree =
let
!alignment :: Int
alignment = OptionSet -> TestTree -> Int
computeAlignment OptionSet
opts TestTree
tree
runSingleTest
:: (IsTest t, ?colors :: Bool)
=> OptionSet -> TestName -> t -> Ap (Reader Level) TestOutput
runSingleTest :: forall t.
(IsTest t, ?colors::Bool) =>
OptionSet -> TestName -> t -> Ap (ReaderT Int Identity) TestOutput
runSingleTest OptionSet
_opts TestName
name t
_test = forall (f :: * -> *) a. f a -> Ap f a
Ap forall a b. (a -> b) -> a -> b
$ do
Int
level <- forall (m :: * -> *) r. Monad m => ReaderT r m r
ask
let
printTestName :: IO ()
printTestName = do
forall r. PrintfType r => TestName -> r
printf TestName
"%s%s: %s" (Int -> TestName
indent Int
level) TestName
name
(forall a. Int -> a -> [a]
replicate (Int
alignment forall a. Num a => a -> a -> a
- Int
indentSize forall a. Num a => a -> a -> a
* Int
level forall a. Num a => a -> a -> a
- TestName -> Int
stringWidth TestName
name) Char
' ')
Handle -> IO ()
hFlush Handle
stdout
printTestResult :: Result -> IO ()
printTestResult Result
result = do
TestName
rDesc <- TestName -> IO TestName
formatMessage forall a b. (a -> b) -> a -> b
$ Result -> TestName
resultDescription Result
result
let
printFn :: TestName -> IO ()
printFn =
case Result -> Outcome
resultOutcome Result
result of
Outcome
Success -> (?colors::Bool) => TestName -> IO ()
ok
Failure FailureReason
TestDepFailed -> (?colors::Bool) => TestName -> IO ()
skipped
Outcome
_ -> (?colors::Bool) => TestName -> IO ()
fail
time :: Time
time = Result -> Time
resultTime Result
result
TestName -> IO ()
printFn (Result -> TestName
resultShortDescription Result
result)
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Time
time forall a. Ord a => a -> a -> Bool
>= Time
0.01) forall a b. (a -> b) -> a -> b
$
TestName -> IO ()
printFn (forall r. PrintfType r => TestName -> r
printf TestName
" (%.2fs)" Time
time)
TestName -> IO ()
printFn TestName
"\n"
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Bool -> Bool
not forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) a. Foldable t => t a -> Bool
null TestName
rDesc) forall a b. (a -> b) -> a -> b
$
(if Result -> Bool
resultSuccessful Result
result then (?colors::Bool) => TestName -> IO ()
infoOk else (?colors::Bool) => TestName -> IO ()
infoFail) forall a b. (a -> b) -> a -> b
$
forall r. PrintfType r => TestName -> r
printf TestName
"%s%s\n" (Int -> TestName
indent forall a b. (a -> b) -> a -> b
$ Int
level forall a. Num a => a -> a -> a
+ Int
1) (Int -> TestName -> TestName
formatDesc (Int
levelforall a. Num a => a -> a -> a
+Int
1) TestName
rDesc)
case Result -> ResultDetailsPrinter
resultDetailsPrinter Result
result of
ResultDetailsPrinter Int -> ConsoleFormatPrinter -> IO ()
action -> Int -> ConsoleFormatPrinter -> IO ()
action Int
level (?colors::Bool) => ConsoleFormatPrinter
withConsoleFormat
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ TestName -> IO () -> (Result -> IO ()) -> TestOutput
PrintTest TestName
name IO ()
printTestName (?colors::Bool) => Result -> IO ()
printTestResult
runGroup :: OptionSet -> TestName -> Ap (Reader Level) TestOutput -> Ap (Reader Level) TestOutput
runGroup :: OptionSet
-> TestName
-> Ap (ReaderT Int Identity) TestOutput
-> Ap (ReaderT Int Identity) TestOutput
runGroup OptionSet
_opts TestName
name Ap (ReaderT Int Identity) TestOutput
grp = forall (f :: * -> *) a. f a -> Ap f a
Ap forall a b. (a -> b) -> a -> b
$ do
Int
level <- forall (m :: * -> *) r. Monad m => ReaderT r m r
ask
let
printHeading :: IO ()
printHeading = forall r. PrintfType r => TestName -> r
printf TestName
"%s%s\n" (Int -> TestName
indent Int
level) TestName
name
printBody :: TestOutput
printBody = forall r a. Reader r a -> r -> a
runReader (forall (f :: * -> *) a. Ap f a -> f a
getApp Ap (ReaderT Int Identity) TestOutput
grp) (Int
level forall a. Num a => a -> a -> a
+ Int
1)
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ TestName -> IO () -> TestOutput -> TestOutput
PrintHeading TestName
name IO ()
printHeading TestOutput
printBody
in
forall a b c. (a -> b -> c) -> b -> a -> c
flip forall r a. Reader r a -> r -> a
runReader Int
0 forall a b. (a -> b) -> a -> b
$ forall (f :: * -> *) a. Ap f a -> f a
getApp forall a b. (a -> b) -> a -> b
$
forall b. Monoid b => TreeFold b -> OptionSet -> TestTree -> b
foldTestTree
forall b. Monoid b => TreeFold b
trivialFold
{ foldSingle :: forall t.
IsTest t =>
OptionSet -> TestName -> t -> Ap (ReaderT Int Identity) TestOutput
foldSingle = forall t.
(IsTest t, ?colors::Bool) =>
OptionSet -> TestName -> t -> Ap (ReaderT Int Identity) TestOutput
runSingleTest
, foldGroup :: OptionSet
-> TestName
-> Ap (ReaderT Int Identity) TestOutput
-> Ap (ReaderT Int Identity) TestOutput
foldGroup = OptionSet
-> TestName
-> Ap (ReaderT Int Identity) TestOutput
-> Ap (ReaderT Int Identity) TestOutput
runGroup
}
OptionSet
opts TestTree
tree
foldTestOutput
:: Monoid b
=> (String -> IO () -> IO Result -> (Result -> IO ()) -> b)
-> (String -> IO () -> b -> b)
-> TestOutput
-> StatusMap
-> b
foldTestOutput :: forall b.
Monoid b =>
(TestName -> IO () -> IO Result -> (Result -> IO ()) -> b)
-> (TestName -> IO () -> b -> b) -> TestOutput -> StatusMap -> b
foldTestOutput TestName -> IO () -> IO Result -> (Result -> IO ()) -> b
foldTest TestName -> IO () -> b -> b
foldHeading TestOutput
outputTree StatusMap
smap =
forall a b c. (a -> b -> c) -> b -> a -> c
flip forall s a. State s a -> s -> a
evalState Int
0 forall a b. (a -> b) -> a -> b
$ forall (f :: * -> *) a. Ap f a -> f a
getApp forall a b. (a -> b) -> a -> b
$ forall {m :: * -> *}. Monad m => TestOutput -> Ap (StateT Int m) b
go TestOutput
outputTree where
go :: TestOutput -> Ap (StateT Int m) b
go (PrintTest TestName
name IO ()
printName Result -> IO ()
printResult) = forall (f :: * -> *) a. f a -> Ap f a
Ap forall a b. (a -> b) -> a -> b
$ do
Int
ix <- forall (m :: * -> *) s. Monad m => StateT s m s
get
forall (m :: * -> *) s. Monad m => s -> StateT s m ()
put forall a b. (a -> b) -> a -> b
$! Int
ix forall a. Num a => a -> a -> a
+ Int
1
let
statusVar :: TVar Status
statusVar =
forall a. a -> Maybe a -> a
fromMaybe (forall a. HasCallStack => TestName -> a
error TestName
"internal error: index out of bounds") forall a b. (a -> b) -> a -> b
$
forall a. Int -> IntMap a -> Maybe a
IntMap.lookup Int
ix StatusMap
smap
readStatusVar :: IO Result
readStatusVar = TVar Status -> IO Result
getResultFromTVar TVar Status
statusVar
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ TestName -> IO () -> IO Result -> (Result -> IO ()) -> b
foldTest TestName
name IO ()
printName IO Result
readStatusVar Result -> IO ()
printResult
go (PrintHeading TestName
name IO ()
printName TestOutput
printBody) = forall (f :: * -> *) a. f a -> Ap f a
Ap forall a b. (a -> b) -> a -> b
$
TestName -> IO () -> b -> b
foldHeading TestName
name IO ()
printName forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (f :: * -> *) a. Ap f a -> f a
getApp (TestOutput -> Ap (StateT Int m) b
go TestOutput
printBody)
go (Seq TestOutput
a TestOutput
b) = forall a. Monoid a => a -> a -> a
mappend (TestOutput -> Ap (StateT Int m) b
go TestOutput
a) (TestOutput -> Ap (StateT Int m) b
go TestOutput
b)
go TestOutput
Skip = forall a. Monoid a => a
mempty
consoleOutput :: (?colors :: Bool) => TestOutput -> StatusMap -> IO ()
consoleOutput :: (?colors::Bool) => TestOutput -> StatusMap -> IO ()
consoleOutput TestOutput
toutput StatusMap
smap =
forall (f :: * -> *). Traversal f -> f ()
getTraversal forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a, b) -> a
fst forall a b. (a -> b) -> a -> b
$ forall b.
Monoid b =>
(TestName -> IO () -> IO Result -> (Result -> IO ()) -> b)
-> (TestName -> IO () -> b -> b) -> TestOutput -> StatusMap -> b
foldTestOutput forall {p} {t}.
p -> IO () -> IO t -> (t -> IO ()) -> (Traversal IO, Any)
foldTest forall {p}.
p -> IO () -> (Traversal IO, Any) -> (Traversal IO, Any)
foldHeading TestOutput
toutput StatusMap
smap
where
foldTest :: p -> IO () -> IO t -> (t -> IO ()) -> (Traversal IO, Any)
foldTest p
_name IO ()
printName IO t
getResult t -> IO ()
printResult =
( forall (f :: * -> *). f () -> Traversal f
Traversal forall a b. (a -> b) -> a -> b
$ do
IO ()
printName :: IO ()
t
r <- IO t
getResult
t -> IO ()
printResult t
r
, Bool -> Any
Any Bool
True)
foldHeading :: p -> IO () -> (Traversal IO, Any) -> (Traversal IO, Any)
foldHeading p
_name IO ()
printHeading (Traversal IO
printBody, Any Bool
nonempty) =
( forall (f :: * -> *). f () -> Traversal f
Traversal forall a b. (a -> b) -> a -> b
$ do
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when Bool
nonempty forall a b. (a -> b) -> a -> b
$ do IO ()
printHeading :: IO (); forall (f :: * -> *). Traversal f -> f ()
getTraversal Traversal IO
printBody
, Bool -> Any
Any Bool
nonempty
)
consoleOutputHidingSuccesses :: (?colors :: Bool) => TestOutput -> StatusMap -> IO ()
consoleOutputHidingSuccesses :: (?colors::Bool) => TestOutput -> StatusMap -> IO ()
consoleOutputHidingSuccesses TestOutput
toutput StatusMap
smap =
forall (f :: * -> *) a. Functor f => f a -> f ()
void forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) a. Ap f a -> f a
getApp forall a b. (a -> b) -> a -> b
$ forall b.
Monoid b =>
(TestName -> IO () -> IO Result -> (Result -> IO ()) -> b)
-> (TestName -> IO () -> b -> b) -> TestOutput -> StatusMap -> b
foldTestOutput forall {p}.
p -> IO () -> IO Result -> (Result -> IO ()) -> Ap IO Any
foldTest forall {p}. p -> IO () -> Ap IO Any -> Ap IO Any
foldHeading TestOutput
toutput StatusMap
smap
where
foldTest :: p -> IO () -> IO Result -> (Result -> IO ()) -> Ap IO Any
foldTest p
_name IO ()
printName IO Result
getResult Result -> IO ()
printResult =
forall (f :: * -> *) a. f a -> Ap f a
Ap forall a b. (a -> b) -> a -> b
$ do
IO ()
printName :: IO ()
Result
r <- IO Result
getResult
if Result -> Bool
resultSuccessful Result
r
then do IO ()
clearThisLine; forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ Bool -> Any
Any Bool
False
else do Result -> IO ()
printResult Result
r :: IO (); forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ Bool -> Any
Any Bool
True
foldHeading :: p -> IO () -> Ap IO Any -> Ap IO Any
foldHeading p
_name IO ()
printHeading Ap IO Any
printBody =
forall (f :: * -> *) a. f a -> Ap f a
Ap forall a b. (a -> b) -> a -> b
$ do
IO ()
printHeading :: IO ()
Any Bool
failed <- forall (f :: * -> *) a. Ap f a -> f a
getApp Ap IO Any
printBody
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless Bool
failed IO ()
clearAboveLine
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ Bool -> Any
Any Bool
failed
clearAboveLine :: IO ()
clearAboveLine = do Int -> IO ()
cursorUpLine Int
1; IO ()
clearThisLine
clearThisLine :: IO ()
clearThisLine = do IO ()
clearLine; Int -> IO ()
setCursorColumn Int
0
streamOutputHidingSuccesses :: (?colors :: Bool) => TestOutput -> StatusMap -> IO ()
streamOutputHidingSuccesses :: (?colors::Bool) => TestOutput -> StatusMap -> IO ()
streamOutputHidingSuccesses TestOutput
toutput StatusMap
smap =
forall (f :: * -> *) a. Functor f => f a -> f ()
void forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b c. (a -> b -> c) -> b -> a -> c
flip forall (m :: * -> *) s a. Monad m => StateT s m a -> s -> m a
evalStateT [] forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) a. Ap f a -> f a
getApp forall a b. (a -> b) -> a -> b
$
forall b.
Monoid b =>
(TestName -> IO () -> IO Result -> (Result -> IO ()) -> b)
-> (TestName -> IO () -> b -> b) -> TestOutput -> StatusMap -> b
foldTestOutput forall {m :: * -> *} {p} {a}.
MonadIO m =>
p
-> IO ()
-> IO Result
-> (Result -> IO ())
-> Ap (StateT [IO a] m) Any
foldTest forall {m :: * -> *} {p} {a}.
Monad m =>
p -> a -> Ap (StateT [a] m) Any -> Ap (StateT [a] m) Any
foldHeading TestOutput
toutput StatusMap
smap
where
foldTest :: p
-> IO ()
-> IO Result
-> (Result -> IO ())
-> Ap (StateT [IO a] m) Any
foldTest p
_name IO ()
printName IO Result
getResult Result -> IO ()
printResult =
forall (f :: * -> *) a. f a -> Ap f a
Ap forall a b. (a -> b) -> a -> b
$ do
Result
r <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ IO Result
getResult
if Result -> Bool
resultSuccessful Result
r
then forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ Bool -> Any
Any Bool
False
else do
[IO a]
stack <- forall (m :: * -> *) s. Monad m => StateT s m s
get
forall (m :: * -> *) s. Monad m => s -> StateT s m ()
put []
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ do
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ forall a b. (a -> b) -> a -> b
$ forall a. [a] -> [a]
reverse [IO a]
stack
IO ()
printName :: IO ()
Result -> IO ()
printResult Result
r :: IO ()
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ Bool -> Any
Any Bool
True
foldHeading :: p -> a -> Ap (StateT [a] m) Any -> Ap (StateT [a] m) Any
foldHeading p
_name a
printHeading Ap (StateT [a] m) Any
printBody =
forall (f :: * -> *) a. f a -> Ap f a
Ap forall a b. (a -> b) -> a -> b
$ do
forall (m :: * -> *) s. Monad m => (s -> s) -> StateT s m ()
modify (a
printHeading forall a. a -> [a] -> [a]
:)
Any Bool
failed <- forall (f :: * -> *) a. Ap f a -> f a
getApp Ap (StateT [a] m) Any
printBody
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless Bool
failed forall a b. (a -> b) -> a -> b
$
forall (m :: * -> *) s. Monad m => (s -> s) -> StateT s m ()
modify forall a b. (a -> b) -> a -> b
$ \[a]
stack ->
case [a]
stack of
a
_:[a]
rest -> [a]
rest
[] -> []
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ Bool -> Any
Any Bool
failed
data Statistics = Statistics
{ Statistics -> Int
statTotal :: !Int
, Statistics -> Int
statFailures :: !Int
}
instance Sem.Semigroup Statistics where
Statistics Int
t1 Int
f1 <> :: Statistics -> Statistics -> Statistics
<> Statistics Int
t2 Int
f2 = Int -> Int -> Statistics
Statistics (Int
t1 forall a. Num a => a -> a -> a
+ Int
t2) (Int
f1 forall a. Num a => a -> a -> a
+ Int
f2)
instance Monoid Statistics where
mempty :: Statistics
mempty = Int -> Int -> Statistics
Statistics Int
0 Int
0
#if !MIN_VERSION_base(4,11,0)
mappend = (Sem.<>)
#endif
computeStatistics :: StatusMap -> IO Statistics
computeStatistics :: StatusMap -> IO Statistics
computeStatistics = forall (f :: * -> *) a. Ap f a -> f a
getApp forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap (\TVar Status
var -> forall (f :: * -> *) a. f a -> Ap f a
Ap forall a b. (a -> b) -> a -> b
$
(\Result
r -> Int -> Int -> Statistics
Statistics Int
1 (if Result -> Bool
resultSuccessful Result
r then Int
0 else Int
1))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> TVar Status -> IO Result
getResultFromTVar TVar Status
var)
reportStatistics :: (?colors :: Bool) => Statistics -> IO ()
reportStatistics :: (?colors::Bool) => Statistics -> IO ()
reportStatistics Statistics
st = case Statistics -> Int
statFailures Statistics
st of
Int
0 -> (?colors::Bool) => TestName -> IO ()
ok forall a b. (a -> b) -> a -> b
$ forall r. PrintfType r => TestName -> r
printf TestName
"All %d tests passed" (Statistics -> Int
statTotal Statistics
st)
Int
fs -> (?colors::Bool) => TestName -> IO ()
fail forall a b. (a -> b) -> a -> b
$ forall r. PrintfType r => TestName -> r
printf TestName
"%d out of %d tests failed" Int
fs (Statistics -> Int
statTotal Statistics
st)
printStatistics :: (?colors :: Bool) => Statistics -> Time -> IO ()
printStatistics :: (?colors::Bool) => Statistics -> Time -> IO ()
printStatistics Statistics
st Time
time = do
forall r. PrintfType r => TestName -> r
printf TestName
"\n"
(?colors::Bool) => Statistics -> IO ()
reportStatistics Statistics
st
case Statistics -> Int
statFailures Statistics
st of
Int
0 -> (?colors::Bool) => TestName -> IO ()
ok forall a b. (a -> b) -> a -> b
$ forall r. PrintfType r => TestName -> r
printf TestName
" (%.2fs)\n" Time
time
Int
_ -> (?colors::Bool) => TestName -> IO ()
fail forall a b. (a -> b) -> a -> b
$ forall r. PrintfType r => TestName -> r
printf TestName
" (%.2fs)\n" Time
time
printStatisticsNoTime :: (?colors :: Bool) => Statistics -> IO ()
printStatisticsNoTime :: (?colors::Bool) => Statistics -> IO ()
printStatisticsNoTime Statistics
st = (?colors::Bool) => Statistics -> IO ()
reportStatistics Statistics
st forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> forall r. PrintfType r => TestName -> r
printf TestName
"\n"
statusMapResult
:: Int
-> StatusMap
-> IO Bool
statusMapResult :: Int -> StatusMap -> IO Bool
statusMapResult Int
lookahead0 StatusMap
smap
| forall a. IntMap a -> Bool
IntMap.null StatusMap
smap = forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
| Bool
otherwise =
forall (m :: * -> *) a. Monad m => m (m a) -> m a
join forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. STM a -> IO a
atomically forall a b. (a -> b) -> a -> b
$
forall a b. (Int -> a -> b -> b) -> b -> IntMap a -> b
IntMap.foldrWithKey Int
-> TVar Status
-> (IntMap () -> Int -> STM (IO Bool))
-> IntMap ()
-> Int
-> STM (IO Bool)
f IntMap () -> Int -> STM (IO Bool)
finish StatusMap
smap forall a. Monoid a => a
mempty Int
lookahead0
where
f :: Int
-> TVar Status
-> (IntMap.IntMap () -> Int -> STM (IO Bool))
-> (IntMap.IntMap () -> Int -> STM (IO Bool))
f :: Int
-> TVar Status
-> (IntMap () -> Int -> STM (IO Bool))
-> IntMap ()
-> Int
-> STM (IO Bool)
f Int
key TVar Status
tvar IntMap () -> Int -> STM (IO Bool)
k IntMap ()
ok_tests Int
lookahead
| Int
lookahead forall a. Ord a => a -> a -> Bool
<= Int
0 =
IntMap () -> STM (IO Bool)
next_iter IntMap ()
ok_tests
| Bool
otherwise = do
Status
this_status <- forall a. TVar a -> STM a
readTVar TVar Status
tvar
case Status
this_status of
Done Result
r ->
if Result -> Bool
resultSuccessful Result
r
then IntMap () -> Int -> STM (IO Bool)
k (forall a. Int -> a -> IntMap a -> IntMap a
IntMap.insert Int
key () IntMap ()
ok_tests) Int
lookahead
else forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
Status
_ -> IntMap () -> Int -> STM (IO Bool)
k IntMap ()
ok_tests (Int
lookaheadforall a. Num a => a -> a -> a
-Int
1)
next_iter :: IntMap.IntMap () -> STM (IO Bool)
next_iter :: IntMap () -> STM (IO Bool)
next_iter IntMap ()
ok_tests =
if forall a. IntMap a -> Bool
IntMap.null IntMap ()
ok_tests
then forall a. STM a
retry
else forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ Int -> StatusMap -> IO Bool
statusMapResult Int
lookahead0 (forall a b. IntMap a -> IntMap b -> IntMap a
IntMap.difference StatusMap
smap IntMap ()
ok_tests)
finish :: IntMap.IntMap () -> Int -> STM (IO Bool)
finish :: IntMap () -> Int -> STM (IO Bool)
finish IntMap ()
ok_tests Int
_ = IntMap () -> STM (IO Bool)
next_iter IntMap ()
ok_tests
consoleTestReporter :: Ingredient
consoleTestReporter :: Ingredient
consoleTestReporter = [OptionDescription]
-> (OptionSet
-> TestTree -> Maybe (StatusMap -> IO (Time -> IO Bool)))
-> Ingredient
TestReporter [OptionDescription]
consoleTestReporterOptions forall a b. (a -> b) -> a -> b
$ \OptionSet
opts TestTree
tree ->
let
TestPattern Maybe Expr
pattern = forall v. IsOption v => OptionSet -> v
lookupOption OptionSet
opts
tests :: [TestName]
tests = OptionSet -> TestTree -> [TestName]
testsNames OptionSet
opts TestTree
tree
hook :: [TestName] -> Result -> IO Result
hook = (forall (m :: * -> *) a. Monad m => a -> m a
return forall b c a. (b -> c) -> (a -> b) -> a -> c
.) forall b c a. (b -> c) -> (a -> b) -> a -> c
. [TestName] -> Maybe Expr -> [TestName] -> Result -> Result
appendPatternIfTestFailed [TestName]
tests Maybe Expr
pattern
TestReporter [OptionDescription]
_ OptionSet -> TestTree -> Maybe (StatusMap -> IO (Time -> IO Bool))
cb = ([TestName] -> Result -> IO Result) -> Ingredient
consoleTestReporterWithHook [TestName] -> Result -> IO Result
hook
in OptionSet -> TestTree -> Maybe (StatusMap -> IO (Time -> IO Bool))
cb OptionSet
opts TestTree
tree
appendPatternIfTestFailed
:: [TestName]
-> Maybe Expr
-> [TestName]
-> Result
-> Result
appendPatternIfTestFailed :: [TestName] -> Maybe Expr -> [TestName] -> Result -> Result
appendPatternIfTestFailed [TestName
_] Maybe Expr
_ [TestName]
_ Result
res = Result
res
appendPatternIfTestFailed [TestName]
_ Maybe Expr
_ [] Result
res = Result
res
appendPatternIfTestFailed [TestName]
tests Maybe Expr
currentPattern (TestName
name : [TestName]
names) Result
res = case Result -> Outcome
resultOutcome Result
res of
Outcome
Success -> Result
res
Failure{} -> Result
res { resultDescription :: TestName
resultDescription = Result -> TestName
resultDescription Result
res forall a. [a] -> [a] -> [a]
++ TestName
msg }
where
msg :: TestName
msg = TestName
"\nUse -p '" forall a. [a] -> [a] -> [a]
++ TestName -> TestName
escapeQuotes (Expr -> TestName
printAwkExpr Expr
pattern) forall a. [a] -> [a] -> [a]
++ TestName
"' to rerun this test only."
escapeQuotes :: TestName -> TestName
escapeQuotes = forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap forall a b. (a -> b) -> a -> b
$ \Char
c -> if Char
c forall a. Eq a => a -> a -> Bool
== Char
'\'' then TestName
"'\\''" else [Char
c]
findPattern :: [TestName] -> TestName -> [TestName] -> Expr
findPattern [TestName
_] TestName
pat [TestName]
_ = TestName -> Expr
ERE TestName
pat
findPattern [TestName]
_ TestName
pat [] = Expr -> Expr -> Expr
EQ (Expr -> Expr
Field (Int -> Expr
IntLit Int
0)) (TestName -> Expr
StringLit TestName
pat)
findPattern [TestName]
ts TestName
pat (TestName
n : [TestName]
ns) = let pat' :: TestName
pat' = TestName
n forall a. [a] -> [a] -> [a]
++ Char
'.' forall a. a -> [a] -> [a]
: TestName
pat in
[TestName] -> TestName -> [TestName] -> Expr
findPattern (forall a. (a -> Bool) -> [a] -> [a]
filter (TestName
pat' forall a. Eq a => [a] -> [a] -> Bool
`isInfixOf`) [TestName]
ts) TestName
pat' [TestName]
ns
individualPattern :: Expr
individualPattern = [TestName] -> TestName -> [TestName] -> Expr
findPattern (forall a. (a -> Bool) -> [a] -> [a]
filter (TestName
name forall a. Eq a => [a] -> [a] -> Bool
`isInfixOf`) [TestName]
tests) TestName
name [TestName]
names
pattern :: Expr
pattern = forall b a. b -> (a -> b) -> Maybe a -> b
maybe forall a. a -> a
id Expr -> Expr -> Expr
And Maybe Expr
currentPattern Expr
individualPattern
consoleTestReporterOptions :: [OptionDescription]
consoleTestReporterOptions :: [OptionDescription]
consoleTestReporterOptions =
[ forall v. IsOption v => Proxy v -> OptionDescription
Option (forall {k} (t :: k). Proxy t
Proxy :: Proxy Quiet)
, forall v. IsOption v => Proxy v -> OptionDescription
Option (forall {k} (t :: k). Proxy t
Proxy :: Proxy HideSuccesses)
, forall v. IsOption v => Proxy v -> OptionDescription
Option (forall {k} (t :: k). Proxy t
Proxy :: Proxy UseColor)
, forall v. IsOption v => Proxy v -> OptionDescription
Option (forall {k} (t :: k). Proxy t
Proxy :: Proxy AnsiTricks)
]
consoleTestReporterWithHook :: ([TestName] -> Result -> IO Result) -> Ingredient
consoleTestReporterWithHook :: ([TestName] -> Result -> IO Result) -> Ingredient
consoleTestReporterWithHook [TestName] -> Result -> IO Result
hook = [OptionDescription]
-> (OptionSet
-> TestTree -> Maybe (StatusMap -> IO (Time -> IO Bool)))
-> Ingredient
TestReporter [OptionDescription]
consoleTestReporterOptions forall a b. (a -> b) -> a -> b
$
\OptionSet
opts TestTree
tree -> forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ \StatusMap
smap -> do
let
whenColor :: UseColor
whenColor = forall v. IsOption v => OptionSet -> v
lookupOption OptionSet
opts
Quiet Bool
quiet = forall v. IsOption v => OptionSet -> v
lookupOption OptionSet
opts
HideSuccesses Bool
hideSuccesses = forall v. IsOption v => OptionSet -> v
lookupOption OptionSet
opts
NumThreads Int
numThreads = forall v. IsOption v => OptionSet -> v
lookupOption OptionSet
opts
AnsiTricks Bool
ansiTricks = forall v. IsOption v => OptionSet -> v
lookupOption OptionSet
opts
if Bool
quiet
then do
Bool
b <- Int -> StatusMap -> IO Bool
statusMapResult Int
numThreads StatusMap
smap
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ \Time
_time -> forall (m :: * -> *) a. Monad m => a -> m a
return Bool
b
else
do
Bool
isTerm <- Handle -> IO Bool
hSupportsANSI Handle
stdout
Bool
isTermColor <- Handle -> IO Bool
hSupportsANSIColor Handle
stdout
(\IO (Time -> IO Bool)
k -> if Bool
isTerm
then (do IO ()
hideCursor; IO (Time -> IO Bool)
k) forall a b. IO a -> IO b -> IO a
`finally` IO ()
showCursor
else IO (Time -> IO Bool)
k) forall a b. (a -> b) -> a -> b
$ do
Handle -> BufferMode -> IO ()
hSetBuffering Handle
stdout BufferMode
LineBuffering
let
?colors = UseColor -> Bool -> Bool
useColor UseColor
whenColor Bool
isTermColor
let
toutput :: TestOutput
toutput = ([TestName] -> Result -> IO Result) -> TestOutput -> TestOutput
applyHook [TestName] -> Result -> IO Result
hook forall a b. (a -> b) -> a -> b
$ (?colors::Bool) => OptionSet -> TestTree -> TestOutput
buildTestOutput OptionSet
opts TestTree
tree
case () of { ()
_
| Bool
hideSuccesses Bool -> Bool -> Bool
&& Bool
isTerm Bool -> Bool -> Bool
&& Bool
ansiTricks ->
(?colors::Bool) => TestOutput -> StatusMap -> IO ()
consoleOutputHidingSuccesses TestOutput
toutput StatusMap
smap
| Bool
hideSuccesses ->
(?colors::Bool) => TestOutput -> StatusMap -> IO ()
streamOutputHidingSuccesses TestOutput
toutput StatusMap
smap
| Bool
otherwise -> (?colors::Bool) => TestOutput -> StatusMap -> IO ()
consoleOutput TestOutput
toutput StatusMap
smap
}
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ \Time
time -> do
Statistics
stats <- StatusMap -> IO Statistics
computeStatistics StatusMap
smap
(?colors::Bool) => Statistics -> Time -> IO ()
printStatistics Statistics
stats Time
time
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ Statistics -> Int
statFailures Statistics
stats forall a. Eq a => a -> a -> Bool
== Int
0
newtype Quiet = Quiet Bool
deriving (Quiet -> Quiet -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Quiet -> Quiet -> Bool
$c/= :: Quiet -> Quiet -> Bool
== :: Quiet -> Quiet -> Bool
$c== :: Quiet -> Quiet -> Bool
Eq, Eq Quiet
Quiet -> Quiet -> Bool
Quiet -> Quiet -> Ordering
Quiet -> Quiet -> Quiet
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Quiet -> Quiet -> Quiet
$cmin :: Quiet -> Quiet -> Quiet
max :: Quiet -> Quiet -> Quiet
$cmax :: Quiet -> Quiet -> Quiet
>= :: Quiet -> Quiet -> Bool
$c>= :: Quiet -> Quiet -> Bool
> :: Quiet -> Quiet -> Bool
$c> :: Quiet -> Quiet -> Bool
<= :: Quiet -> Quiet -> Bool
$c<= :: Quiet -> Quiet -> Bool
< :: Quiet -> Quiet -> Bool
$c< :: Quiet -> Quiet -> Bool
compare :: Quiet -> Quiet -> Ordering
$ccompare :: Quiet -> Quiet -> Ordering
Ord, Typeable)
instance IsOption Quiet where
defaultValue :: Quiet
defaultValue = Bool -> Quiet
Quiet Bool
False
parseValue :: TestName -> Maybe Quiet
parseValue = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Bool -> Quiet
Quiet forall b c a. (b -> c) -> (a -> b) -> a -> c
. TestName -> Maybe Bool
safeReadBool
optionName :: Tagged Quiet TestName
optionName = forall (m :: * -> *) a. Monad m => a -> m a
return TestName
"quiet"
optionHelp :: Tagged Quiet TestName
optionHelp = forall (m :: * -> *) a. Monad m => a -> m a
return TestName
"Do not produce any output; indicate success only by the exit code"
optionCLParser :: Parser Quiet
optionCLParser = forall v. IsOption v => Mod FlagFields v -> v -> Parser v
mkFlagCLParser (forall (f :: * -> *) a. HasName f => Char -> Mod f a
short Char
'q') (Bool -> Quiet
Quiet Bool
True)
newtype HideSuccesses = HideSuccesses Bool
deriving (HideSuccesses -> HideSuccesses -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: HideSuccesses -> HideSuccesses -> Bool
$c/= :: HideSuccesses -> HideSuccesses -> Bool
== :: HideSuccesses -> HideSuccesses -> Bool
$c== :: HideSuccesses -> HideSuccesses -> Bool
Eq, Eq HideSuccesses
HideSuccesses -> HideSuccesses -> Bool
HideSuccesses -> HideSuccesses -> Ordering
HideSuccesses -> HideSuccesses -> HideSuccesses
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: HideSuccesses -> HideSuccesses -> HideSuccesses
$cmin :: HideSuccesses -> HideSuccesses -> HideSuccesses
max :: HideSuccesses -> HideSuccesses -> HideSuccesses
$cmax :: HideSuccesses -> HideSuccesses -> HideSuccesses
>= :: HideSuccesses -> HideSuccesses -> Bool
$c>= :: HideSuccesses -> HideSuccesses -> Bool
> :: HideSuccesses -> HideSuccesses -> Bool
$c> :: HideSuccesses -> HideSuccesses -> Bool
<= :: HideSuccesses -> HideSuccesses -> Bool
$c<= :: HideSuccesses -> HideSuccesses -> Bool
< :: HideSuccesses -> HideSuccesses -> Bool
$c< :: HideSuccesses -> HideSuccesses -> Bool
compare :: HideSuccesses -> HideSuccesses -> Ordering
$ccompare :: HideSuccesses -> HideSuccesses -> Ordering
Ord, Typeable)
instance IsOption HideSuccesses where
defaultValue :: HideSuccesses
defaultValue = Bool -> HideSuccesses
HideSuccesses Bool
False
parseValue :: TestName -> Maybe HideSuccesses
parseValue = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Bool -> HideSuccesses
HideSuccesses forall b c a. (b -> c) -> (a -> b) -> a -> c
. TestName -> Maybe Bool
safeReadBool
optionName :: Tagged HideSuccesses TestName
optionName = forall (m :: * -> *) a. Monad m => a -> m a
return TestName
"hide-successes"
optionHelp :: Tagged HideSuccesses TestName
optionHelp = forall (m :: * -> *) a. Monad m => a -> m a
return TestName
"Do not print tests that passed successfully"
optionCLParser :: Parser HideSuccesses
optionCLParser = forall v. IsOption v => Mod FlagFields v -> v -> Parser v
mkFlagCLParser forall a. Monoid a => a
mempty (Bool -> HideSuccesses
HideSuccesses Bool
True)
data UseColor
= Never
| Always
| Auto
deriving (UseColor -> UseColor -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: UseColor -> UseColor -> Bool
$c/= :: UseColor -> UseColor -> Bool
== :: UseColor -> UseColor -> Bool
$c== :: UseColor -> UseColor -> Bool
Eq, Eq UseColor
UseColor -> UseColor -> Bool
UseColor -> UseColor -> Ordering
UseColor -> UseColor -> UseColor
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: UseColor -> UseColor -> UseColor
$cmin :: UseColor -> UseColor -> UseColor
max :: UseColor -> UseColor -> UseColor
$cmax :: UseColor -> UseColor -> UseColor
>= :: UseColor -> UseColor -> Bool
$c>= :: UseColor -> UseColor -> Bool
> :: UseColor -> UseColor -> Bool
$c> :: UseColor -> UseColor -> Bool
<= :: UseColor -> UseColor -> Bool
$c<= :: UseColor -> UseColor -> Bool
< :: UseColor -> UseColor -> Bool
$c< :: UseColor -> UseColor -> Bool
compare :: UseColor -> UseColor -> Ordering
$ccompare :: UseColor -> UseColor -> Ordering
Ord, Typeable)
instance IsOption UseColor where
defaultValue :: UseColor
defaultValue = UseColor
Auto
parseValue :: TestName -> Maybe UseColor
parseValue = TestName -> Maybe UseColor
parseUseColor
optionName :: Tagged UseColor TestName
optionName = forall (m :: * -> *) a. Monad m => a -> m a
return TestName
"color"
optionHelp :: Tagged UseColor TestName
optionHelp = forall (m :: * -> *) a. Monad m => a -> m a
return TestName
"When to use colored output"
optionCLParser :: Parser UseColor
optionCLParser = forall v. IsOption v => Mod OptionFields v -> Parser v
mkOptionCLParser forall a b. (a -> b) -> a -> b
$ forall (f :: * -> *) a. HasMetavar f => TestName -> Mod f a
metavar TestName
"never|always|auto"
showDefaultValue :: UseColor -> Maybe TestName
showDefaultValue = forall a. a -> Maybe a
Just forall b c a. (b -> c) -> (a -> b) -> a -> c
. UseColor -> TestName
displayUseColor
newtype AnsiTricks = AnsiTricks { AnsiTricks -> Bool
getAnsiTricks :: Bool }
deriving Typeable
instance IsOption AnsiTricks where
defaultValue :: AnsiTricks
defaultValue = Bool -> AnsiTricks
AnsiTricks Bool
True
parseValue :: TestName -> Maybe AnsiTricks
parseValue = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Bool -> AnsiTricks
AnsiTricks forall b c a. (b -> c) -> (a -> b) -> a -> c
. TestName -> Maybe Bool
safeReadBool
optionName :: Tagged AnsiTricks TestName
optionName = forall (m :: * -> *) a. Monad m => a -> m a
return TestName
"ansi-tricks"
optionHelp :: Tagged AnsiTricks TestName
optionHelp = forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$
TestName
"Enable various ANSI terminal tricks. " forall a. [a] -> [a] -> [a]
++
TestName
"Can be set to 'true' or 'false'."
showDefaultValue :: AnsiTricks -> Maybe TestName
showDefaultValue = forall a. a -> Maybe a
Just forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> TestName
displayBool forall b c a. (b -> c) -> (a -> b) -> a -> c
. AnsiTricks -> Bool
getAnsiTricks
displayBool :: Bool -> String
displayBool :: Bool -> TestName
displayBool Bool
b =
case Bool
b of
Bool
False -> TestName
"false"
Bool
True -> TestName
"true"
useColor :: UseColor -> Bool -> Bool
useColor :: UseColor -> Bool -> Bool
useColor UseColor
when_ Bool
isTerm =
case UseColor
when_ of
UseColor
Never -> Bool
False
UseColor
Always -> Bool
True
UseColor
Auto -> Bool
isTerm
parseUseColor :: String -> Maybe UseColor
parseUseColor :: TestName -> Maybe UseColor
parseUseColor TestName
s =
case forall a b. (a -> b) -> [a] -> [b]
map Char -> Char
toLower TestName
s of
TestName
"never" -> forall (m :: * -> *) a. Monad m => a -> m a
return UseColor
Never
TestName
"always" -> forall (m :: * -> *) a. Monad m => a -> m a
return UseColor
Always
TestName
"auto" -> forall (m :: * -> *) a. Monad m => a -> m a
return UseColor
Auto
TestName
_ -> forall a. Maybe a
Nothing
displayUseColor :: UseColor -> String
displayUseColor :: UseColor -> TestName
displayUseColor UseColor
uc =
case UseColor
uc of
UseColor
Never -> TestName
"never"
UseColor
Always -> TestName
"always"
UseColor
Auto -> TestName
"auto"
getResultFromTVar :: TVar Status -> IO Result
getResultFromTVar :: TVar Status -> IO Result
getResultFromTVar TVar Status
var =
forall a. STM a -> IO a
atomically forall a b. (a -> b) -> a -> b
$ do
Status
status <- forall a. TVar a -> STM a
readTVar TVar Status
var
case Status
status of
Done Result
r -> forall (m :: * -> *) a. Monad m => a -> m a
return Result
r
Status
_ -> forall a. STM a
retry
indentSize :: Int
indentSize :: Int
indentSize = Int
2
indent :: Int -> String
indent :: Int -> TestName
indent Int
n = forall a. Int -> a -> [a]
replicate (Int
indentSize forall a. Num a => a -> a -> a
* Int
n) Char
' '
formatDesc
:: Int
-> String
-> String
formatDesc :: Int -> TestName -> TestName
formatDesc Int
n TestName
desc =
let
chomped :: TestName
chomped = forall a. [a] -> [a]
reverse forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. (a -> Bool) -> [a] -> [a]
dropWhile (forall a. Eq a => a -> a -> Bool
== Char
'\n') forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. [a] -> [a]
reverse forall a b. (a -> b) -> a -> b
$ TestName
desc
multiline :: Bool
multiline = Char
'\n' forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` TestName
chomped
paddedDesc :: TestName
paddedDesc = forall a b c. (a -> b -> c) -> b -> a -> c
flip forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap TestName
chomped forall a b. (a -> b) -> a -> b
$ \Char
c ->
if Char
c forall a. Eq a => a -> a -> Bool
== Char
'\n'
then Char
c forall a. a -> [a] -> [a]
: Int -> TestName
indent Int
n
else [Char
c]
in
if Bool
multiline
then TestName
paddedDesc
else TestName
chomped
data Maximum a
= Maximum a
| MinusInfinity
instance Ord a => Sem.Semigroup (Maximum a) where
Maximum a
a <> :: Maximum a -> Maximum a -> Maximum a
<> Maximum a
b = forall a. a -> Maximum a
Maximum (a
a forall a. Ord a => a -> a -> a
`max` a
b)
Maximum a
MinusInfinity <> Maximum a
a = Maximum a
a
Maximum a
a <> Maximum a
MinusInfinity = Maximum a
a
instance Ord a => Monoid (Maximum a) where
mempty :: Maximum a
mempty = forall a. Maximum a
MinusInfinity
#if !MIN_VERSION_base(4,11,0)
mappend = (Sem.<>)
#endif
computeAlignment :: OptionSet -> TestTree -> Int
computeAlignment :: OptionSet -> TestTree -> Int
computeAlignment OptionSet
opts =
forall {t} {a}. (Num t, Num a) => (t -> Maximum a) -> a
fromMonoid forall b c a. (b -> c) -> (a -> b) -> a -> c
.
forall b. Monoid b => TreeFold b -> OptionSet -> TestTree -> b
foldTestTree
forall b. Monoid b => TreeFold b
trivialFold
{ foldSingle :: forall t.
IsTest t =>
OptionSet -> TestName -> t -> Int -> Maximum Int
foldSingle = \OptionSet
_ TestName
name t
_ Int
level -> forall a. a -> Maximum a
Maximum (TestName -> Int
stringWidth TestName
name forall a. Num a => a -> a -> a
+ Int
level)
, foldGroup :: OptionSet -> TestName -> (Int -> Maximum Int) -> Int -> Maximum Int
foldGroup = \OptionSet
_opts TestName
_ Int -> Maximum Int
m -> Int -> Maximum Int
m forall b c a. (b -> c) -> (a -> b) -> a -> c
. (forall a. Num a => a -> a -> a
+ Int
indentSize)
}
OptionSet
opts
where
fromMonoid :: (t -> Maximum a) -> a
fromMonoid t -> Maximum a
m =
case t -> Maximum a
m t
0 of
Maximum a
MinusInfinity -> a
0
Maximum a
x -> a
x
stringWidth :: String -> Int
#ifdef VERSION_wcwidth
stringWidth :: TestName -> Int
stringWidth = forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
Prelude.sum forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map Char -> Int
charWidth
where charWidth :: Char -> Int
charWidth Char
c = case Char -> Int
wcwidth Char
c of
-1 -> Int
1
Int
w -> Int
w
#else
stringWidth = length
#endif
ok, fail, skipped, infoOk, infoFail :: (?colors :: Bool) => String -> IO ()
fail :: (?colors::Bool) => TestName -> IO ()
fail = (?colors::Bool) => ConsoleFormat -> TestName -> IO ()
output ConsoleFormat
failFormat
ok :: (?colors::Bool) => TestName -> IO ()
ok = (?colors::Bool) => ConsoleFormat -> TestName -> IO ()
output ConsoleFormat
okFormat
skipped :: (?colors::Bool) => TestName -> IO ()
skipped = (?colors::Bool) => ConsoleFormat -> TestName -> IO ()
output ConsoleFormat
skippedFormat
infoOk :: (?colors::Bool) => TestName -> IO ()
infoOk = (?colors::Bool) => ConsoleFormat -> TestName -> IO ()
output ConsoleFormat
infoOkFormat
infoFail :: (?colors::Bool) => TestName -> IO ()
infoFail = (?colors::Bool) => ConsoleFormat -> TestName -> IO ()
output ConsoleFormat
infoFailFormat
output
:: (?colors :: Bool)
=> ConsoleFormat
-> String
-> IO ()
output :: (?colors::Bool) => ConsoleFormat -> TestName -> IO ()
output ConsoleFormat
format = (?colors::Bool) => ConsoleFormatPrinter
withConsoleFormat ConsoleFormat
format forall b c a. (b -> c) -> (a -> b) -> a -> c
. TestName -> IO ()
putStr
withConsoleFormat :: (?colors :: Bool) => ConsoleFormatPrinter
withConsoleFormat :: (?colors::Bool) => ConsoleFormatPrinter
withConsoleFormat ConsoleFormat
format IO ()
action
| ?colors::Bool
?colors =
(do
[SGR] -> IO ()
setSGR
[ ConsoleLayer -> ColorIntensity -> Color -> SGR
SetColor ConsoleLayer
Foreground (ConsoleFormat -> ColorIntensity
colorIntensity ConsoleFormat
format) (ConsoleFormat -> Color
color ConsoleFormat
format)
, ConsoleIntensity -> SGR
SetConsoleIntensity (ConsoleFormat -> ConsoleIntensity
consoleIntensity ConsoleFormat
format)
]
IO ()
action
) forall a b. IO a -> IO b -> IO a
`finally` [SGR] -> IO ()
setSGR []
| Bool
otherwise = IO ()
action