module Main (main) where import Control.Monad (join) import Data.Coerce (coerce) import Data.Either.Validation (Validation (..), validationToEither) import Data.Functor.Foldable (Base, Corecursive, Recursive, cata) import qualified Dhall as D import qualified Dhall.Core as D import Dhall.Deriving.Recursive (RecADT (RecADT)) import GHC.Generics (Generic) import Hedgehog import qualified Hedgehog.Gen as Gen import qualified Hedgehog.Range as Range import Numeric.Natural (Natural) import Test.Tasty import Test.Tasty.HUnit import Test.Tasty.Hedgehog data Tree a = Leaf a | Branch (Tree a) (Tree a) deriving stock (Show, Eq, Generic) deriving anyclass (Recursive, Corecursive) deriving (D.FromDhall, D.ToDhall) via (RecADT (Tree a)) type instance Base (Tree a) = TreeF a data TreeF a b = LeafF a | BranchF b b deriving stock (Functor, Generic) deriving anyclass (D.FromDhall, D.ToDhall) main :: IO () main = do let fld = ("(./test/tree.dhall)." <>) dhallTree <- D.inputExpr $ fld "exampleTree" dhallLeafList :: Tree Natural -> [Natural] <- D.input D.auto $ fld "leafList" defaultMain . testGroup "tests" $ [ testCase "Haskell → Dhall" $ JExpr dhallTree @=? JExpr do D.embed D.inject haskellTree, testCase "Dhall → Haskell" $ assertValidation haskellTree $ D.extract D.auto dhallTree, testProperty "Dhall function = Haskell function" $ property do t <- forAll genTree dhallLeafList t === haskellLeafList t, testProperty "roundtrip Haskell → Dhall → Haskell" $ property do t <- forAll genTree let roundtrip = D.extract D.auto . D.embed D.inject t' <- evalEither . validationToEither $ roundtrip t t === t', testProperty "roundtrip Dhall → Haskell → Dhall" $ property do t <- D.embed D.inject <$> forAll genTree let roundtrip = fmap (D.embed D.inject) . D.extract (D.auto @(Tree Natural)) t' <- evalEither . validationToEither $ roundtrip t JExpr t === JExpr t' ] where haskellTree :: Tree Natural haskellTree = Branch (Branch (Branch (Leaf 0) (Leaf 5)) (Branch (Leaf 4) (Leaf 3))) (Leaf 6) haskellLeafList :: Tree a -> [a] haskellLeafList = cata \case LeafF a -> [a] BranchF t t' -> t <> t' genTree :: MonadGen m => m (Tree Natural) genTree = Gen.recursive Gen.choice [Leaf <$> Gen.integral_ (Range.linear 0 100)] [join Gen.subterm2 genTree Branch] newtype JExpr s a = JExpr (D.Expr s a) deriving newtype (Show) instance Eq a => Eq (JExpr s a) where (==) = coerce (D.judgmentallyEqual @a @s @s) assertValidation :: (HasCallStack, Show e, Show a, Eq a) => a -> Validation e a -> Assertion assertValidation a (Success a') = a @=? a' assertValidation _ (Failure e) = assertFailure $ show e