tadka-2.0.0.0: test/props/Phase4.hs
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
-- | Phase 4 properties: the one path from Config to a renderer. The exit
-- criterion is that an explicit `withTarget` is never overridden by detection.
module Phase4 (group) where
import Data.Aeson (Value (Object))
import Data.Text (Text)
import qualified Data.Text as T
import Prettyprinter (pretty)
import Hedgehog
(Gen, Group (..), Property, assert, failure, forAll, property,
success, withTests, (===))
import qualified Hedgehog.Gen as Gen
import Tadka
group :: Group
group = Group "Phase 4 - renderer/config scaffolding"
[ ("withTarget override is honoured by selectRenderer", prop_targetOverride)
, ("override survives other setters", prop_overrideSurvivesSetters)
, ("no explicit target defaults to graphical", prop_defaultTarget)
, ("render produces output for each target", prop_renderSmoke)
]
-- A trivial diagnostic: only `message`, everything else defaulted.
data TrivialDiag = TrivialDiag
instance Diagnostic TrivialDiag where
message _ = pretty ("trivial" :: Text)
targetOf :: SomeRenderer -> Target
targetOf (SomeRenderer (Graphical _)) = TGraphical
targetOf (SomeRenderer (Narratable _)) = TNarratable
targetOf (SomeRenderer (Json _)) = TJson
genTarget :: Gen Target
genTarget = Gen.enumBounded
prop_targetOverride :: Property
prop_targetOverride = property $ do
t <- forAll genTarget
targetOf (selectRenderer (withTarget t defaultConfig)) === t
prop_overrideSurvivesSetters :: Property
prop_overrideSurvivesSetters = property $ do
t <- forAll genTarget
let cfg = withColorMode ColorNever
. withUnicodeMode UnicodeAscii
. withRelatedDepthLimit 3
. withTarget t
$ defaultConfig
targetOf (selectRenderer cfg) === t
prop_defaultTarget :: Property
prop_defaultTarget = withTests 1 . property $
targetOf (selectRenderer defaultConfig) === TGraphical
-- Extract each target's output through a pure helper with a concrete return
-- type; matching the GADT existential directly inside the property monad would
-- leave the result type untouchable.
narratableOutput :: Diagnostic e => Config -> e -> Maybe Text
narratableOutput cfg e = case selectRenderer cfg of
SomeRenderer r@(Narratable _) -> Just (render r e)
_ -> Nothing
jsonOutput :: Diagnostic e => Config -> e -> Maybe Value
jsonOutput cfg e = case selectRenderer cfg of
SomeRenderer r@(Json _) -> Just (render r e)
_ -> Nothing
-- Smoke: each target's render path runs and carries the message through.
prop_renderSmoke :: Property
prop_renderSmoke = withTests 1 . property $ do
case narratableOutput (withTarget TNarratable defaultConfig) TrivialDiag of
Just t -> assert ("trivial" `T.isInfixOf` t)
Nothing -> failure
case jsonOutput (withTarget TJson defaultConfig) TrivialDiag of
Just (Object _) -> success
_ -> failure