packages feed

covenant-1.1.0: test/base-functor/Main.hs

module Main (main) where

import Control.Monad.Reader (runReader)
import Covenant.Data
  ( allComponentTypes,
    hasRecursive,
    mkBaseFunctor,
  )
import Covenant.Test
  ( DataDeclFlavor (Poly1PolyThunks),
    DataDeclSet (DataDeclSet),
    prettyDeclSet,
  )
import Covenant.Type ()
import Data.Map.Strict qualified as M
import Optics.Core (view)
import Test.QuickCheck
  ( Arbitrary (arbitrary, shrink),
    Property,
  )
import Test.Tasty (defaultMain, testGroup)
import Test.Tasty.QuickCheck (forAllShrinkShow, testProperty)

main :: IO ()
main =
  defaultMain . testGroup "BaseFunctors" $
    [testProperty "All recursion is replaced in base functor transform" baseFunctorsContainNoRecursion]

baseFunctorsContainNoRecursion :: Property
baseFunctorsContainNoRecursion = forAllShrinkShow (arbitrary @(DataDeclSet 'Poly1PolyThunks)) shrink prettyDeclSet $ \(DataDeclSet decls) ->
  let declMap = M.fromList $ (\x -> (view #datatypeName x, x)) <$> decls
      baseFunctorResults = flip runReader 0 . mkBaseFunctor <$> declMap
   in M.foldlWithKey'
        ( \acc tyNm origDecl ->
            let isTyRecursive = any (\x -> runReader (hasRecursive tyNm x) 0) (allComponentTypes origDecl)
                mBaseFDecl = baseFunctorResults M.! tyNm
             in case mBaseFDecl of
                  -- if we didn't make a base functor then the original type must NOT be recursive
                  Nothing -> not isTyRecursive && acc
                  Just baseFDecl ->
                    -- If we did make a base functor, it should contain no recursion
                    let recursionInBaseF = any (\x -> runReader (hasRecursive (view #datatypeName baseFDecl) x) 0) (allComponentTypes baseFDecl)
                     in not recursionInBaseF && acc
        )
        True
        declMap