module Penny.Steel.TestTree where
import Data.Maybe (isJust)
import qualified Data.Text as X
import Data.Text (Text)
import qualified Data.List.Split as Sp
import qualified Penny.Steel.Chunk as C
import qualified Penny.Steel.Chunk.Switch as Sw
import qualified Penny.Steel.Pdct as Pt
type Pass = Bool
type Name = Text
data TestTree a = TestTree Name (Payload a)
data Payload a
= Group [TestTree a]
| Test (TestFunc a)
type TestFunc a
= Pt.IndentAmt
-> PassVerbosity
-> FailVerbosity
-> [a]
-> Pt.Level
-> (Pass, [C.Chunk])
group :: Name -> [TestTree a] -> TestTree a
group n ts = TestTree n (Group ts)
test :: Name -> TestFunc a -> TestTree a
test n t = TestTree n (Test t)
type PassVerbosity = Verbosity
type FailVerbosity = Verbosity
data Verbosity
= Silent
| PassFail
| FalseSubjects
| TrueSubjects
| DiscardedSubjects
| DiscardedPredicates
deriving (Eq, Ord, Show)
showSubject
:: (a -> X.Text)
-> Verbosity
-> Pt.IndentAmt
-> Pt.Level
-> Pt.Pdct a
-> (a, Maybe Bool)
-> [C.Chunk]
showSubject swr v i l p (s, b) =
let (showSubj, showDisc) = isSubjectAndDiscardsShown v b
renamer txt = X.concat [swr s, " - ", txt]
renamed = Pt.rename renamer p
in if showSubj
then snd $ Pt.evaluate i showDisc s l renamed
else []
isSubjectAndDiscardsShown :: Verbosity -> Maybe Bool -> (Bool, Bool)
isSubjectAndDiscardsShown v b = case v of
Silent -> (False, False)
PassFail -> (False, False)
FalseSubjects -> (not . isTrue $ b, False)
TrueSubjects -> (isJust b, False)
DiscardedSubjects -> (True, False)
DiscardedPredicates -> (True, True)
showTestTitle :: Pt.IndentAmt -> Pt.Level -> Name -> Pass -> [C.Chunk]
showTestTitle i l n p = [idt, open, passFail, close, blank, txt, nl]
where
passFail = C.chunk ts tf
idt = C.chunk C.defaultTextSpec (X.replicate (i * l) " ")
nl = C.chunk C.defaultTextSpec "\n"
(tf, ts) =
if p
then ("PASS", Sw.switchForeground C.color8_f_green
C.color256_f_2 C.defaultTextSpec)
else ("FAIL", Sw.switchForeground C.color8_f_red
C.color256_f_1 C.defaultTextSpec)
open = C.chunk C.defaultTextSpec "["
close = C.chunk C.defaultTextSpec "]"
blank = C.chunk C.defaultTextSpec (X.singleton ' ')
txt = C.chunk C.defaultTextSpec n
isTrue :: Maybe Bool -> Bool
isTrue = maybe False id
eachSubjectMustBeTrue
:: Name
-> (a -> Text)
-> Pt.Pdct a
-> TestTree a
eachSubjectMustBeTrue n swr p = TestTree n (Test tf)
where
tf i pv fv as lvl = (pass, cks)
where
rslts = zip as (map (Pt.eval p) as)
pass = all (isTrue . snd) rslts
v = if pass then pv else fv
cks = tit ++ subjectChunks
tit = if v == Silent then [] else showTestTitle i lvl n pass
subjectChunks =
concatMap (showSubject swr v i (lvl + 1) p) rslts
seriesAtLeastN
:: Name
-> (a -> X.Text)
-> Int
-> Pt.Pdct a
-> TestTree a
seriesAtLeastN n swr count p = TestTree n (Test tf)
where
tf idnt pv fv as l = (pass, cks)
where
pd (_, res) = isTrue res
resultList = take count
. Sp.split (Sp.keepDelimsR (Sp.whenElt pd))
$ zip as (map (Pt.eval p) as)
pass = length resultList >= count
v = if pass then pv else fv
cks = tit ++ subjectChunks
tit = if v == Silent then [] else showTestTitle idnt l n pass
subjectChunks =
concatMap (showSubject swr v idnt (l + 1) p) . concat $ resultList
indent :: Pt.IndentAmt -> Pt.Level -> Text -> C.Chunk
indent amt lvl t = C.chunk ts txt
where
ts = C.defaultTextSpec
txt = X.concat [spaces, t, "\n"]
spaces = X.replicate (amt * lvl) " "
showTestTree
:: Pt.IndentAmt
-> Pt.Level
-> TestTree a
-> [C.Chunk]
showTestTree amt l (TestTree n p) = indent amt l n : children
where
children = case p of
Group ts -> concatMap (showTestTree amt l) ts
Test _ -> []
evalTestTree
:: Pt.IndentAmt
-> Pt.Level
-> PassVerbosity
-> FailVerbosity
-> [a]
-> TestTree a
-> [Either C.Chunk (Pass, [C.Chunk])]
evalTestTree i l pv fv as (TestTree n p) = case p of
Test f -> [Right $ f i pv fv as l]
Group ts -> Left (indent i l n)
: concatMap (evalTestTree i (l + 1) pv fv as) ts