packages feed

tadka-2.0.0.0: test/props/Phase6.hs

{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}

-- | Phase 6 properties: prose-marker interpretation of 'Ann', and a totality
-- smoke check for the narratable handler over the shared generated set.
module Phase6 (group) where

import           Control.Exception          (SomeException, evaluate, try)
import           Control.Monad.IO.Class      (liftIO)
import           Data.Text                   (Text)
import qualified Data.Text                   as T
import qualified Prettyprinter               as PP

import           Hedgehog
import qualified Hedgehog.Gen                as Gen
import qualified Hedgehog.Range              as Range

import           GenDiag                     (genGD, selfJust, selfNothing)
import           Tadka

group :: Group
group = Group "Phase 6 - narratable handler"
  [ ("AnnCode renders quoted in prose",            prop_annCodeQuoted)
  , ("narratable render is total (fuelled)",       prop_totality)
  , ("narratable render is total (pathological)",  prop_totalityCycles)
  ]

-- A diagnostic whose message carries an AnnCode span, to exercise toProseMarker.
data Coded = Coded Text

instance Diagnostic Coded where
  message (Coded ident) = "undefined variable " <> PP.annotate AnnCode (PP.pretty ident)

renderText :: Diagnostic e => e -> Text
renderText e = case selectRenderer (withTarget TNarratable defaultConfig) of
  SomeRenderer r@(Narratable _) -> render r e
  _                             -> ""

-- The narratable handler wraps AnnCode content in double quotes.
prop_annCodeQuoted :: Property
prop_annCodeQuoted = property $ do
  ident <- forAll (Gen.text (Range.linear 1 8) (Gen.enum 'a' 'z'))
  let out = renderText (Coded ident)
  assert (("\"" <> ident <> "\"") `T.isInfixOf` out)

prop_totality :: Property
prop_totality = property $ do
  fuel <- forAll (Gen.int (Range.linear 0 3))
  d    <- forAllWith (const "<generated diagnostic>") (genGD fuel)
  res  <- liftIO (try (evaluate (T.length (renderText d))) :: IO (Either SomeException Int))
  case res of
    Right _ -> success
    Left e  -> annotate' e

prop_totalityCycles :: Property
prop_totalityCycles = withTests 1 . property $ do
  res <- liftIO (try (evaluate (sum (map (T.length . renderText) [selfNothing, selfJust])))
                   :: IO (Either SomeException Int))
  case res of
    Right _ -> success
    Left e  -> annotate' e

annotate' :: SomeException -> PropertyT IO ()
annotate' e = annotate (show e) >> failure