{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
{-# HLINT ignore "Eta reduce" #-}
module Fail
( tests
, gen0
, test
) where

import qualified Control.Carrier.Fail.Either as FailC
import           Control.Effect.Fail as Fail
import           Gen
import           Hedgehog.Range as Range
import qualified Monad
import qualified MonadFix
import           Test.Tasty
import           Test.Tasty.Hedgehog

tests :: TestTree
tests = testGroup "Fail"
  [ testGroup "FailC" $
    [ testMonad
    , testMonadFix
    , testFail
    ] >>= ($ runL FailC.runFail)
  ] where
  testMonad    run = Monad.test    (m (gen0 e) (\ _ _ -> [])) a b c initial run
  testMonadFix run = MonadFix.test (m (gen0 e) (\ _ _ -> [])) a b   initial run
  testFail     run = Fail.test e   (m (gen0 e) (\ _ _ -> [])) a b   initial run
  initial = identity <*> unit
  e = string (Range.linear 0 50) unicode


gen0 :: MonadFail m => GenTerm String -> GenTerm a -> [GenTerm (m a)]
gen0 e _ = [ label "fail" Fail.fail <*> e ]


test
  :: forall m a b f
  .  (MonadFail m, Arg a, Eq b, Show a, Show b, Vary a, Functor f)
  => GenTerm String
  -> GenM m
  -> GenTerm a
  -> GenTerm b
  -> GenTerm (f ())
  -> Run f (Either String) m
  -> [TestTree]
test msg m _ b i (Run runFail) =
  [ testProperty "fail annihilates >>=" . forall (i :. msg :. fn @a (m b) :. Nil) $
    \ i s k -> runFail ((Fail.fail s >>= k) <$ i) === runFail (Fail.fail s <$ i)
  ]