-- | -- Module : Conjure.Defn.Test -- Copyright : (c) 2021-2024 Rudy Matela -- License : 3-Clause BSD (see the file LICENSE) -- Maintainer : Rudy Matela -- -- This module is part of "Conjure". -- -- This module exports functions that test 'Defn's using ground values. -- -- You are probably better off importing "Conjure". module Conjure.Defn.Test ( equalModuloTesting , erroneousCandidate , findDefnError , listDefnErrors ) where import Conjure.Defn import Conjure.Conjurable import Test.LeanCheck.Error (errorToFalse, errorToNothing) import Data.Dynamic (fromDyn, dynApp) equalModuloTesting :: Conjurable f => Int -> Int -> String -> f -> Defn -> Defn -> Bool equalModuloTesting maxTests maxEvalRecursions nm f = (===) where testGrounds = nonNegativeAppGrounds maxTests maxEvalRecursions nm f d1 === d2 = all are $ testGrounds where -- silences errors, ok since this is for optional measuring of optimal pruning are :: Expr -> Bool are e = isError (ee d1 d1 e) && isError (ee d2 d2 e) || errorToFalse (ee d1 d2 e) where ee = devlEqual maxEvalRecursions f -- | For debugging purposes. -- -- This may be taken out of the API at any moment. erroneousCandidate :: Conjurable f => Int -> Int -> String -> f -> Defn -> Bool erroneousCandidate maxTests maxEvalRecursions nm f = isJust . findDefnError maxTests maxEvalRecursions nm f -- | For debugging purposes, -- finds a set of arguments that triggers an error in the candidate 'Defn'. -- -- Warning: this is an experimental function -- which may be taken out of the API at any moment. findDefnError :: Conjurable f => Int -> Int -> String -> f -> Defn -> Maybe Expr findDefnError maxTests maxEvalRecursions nm f = listToMaybe . listDefnErrors maxTests maxEvalRecursions nm f listDefnErrors :: Conjurable f => Int -> Int -> String -> f -> Defn -> [Expr] listDefnErrors maxTests maxEvalRecursions nm f d = filter is testGrounds where testGrounds :: [Expr] testGrounds = nonNegativeAppGrounds maxTests maxEvalRecursions nm f is :: Expr -> Bool is e = isError (devlEqual maxEvalRecursions f d d e) nonNegativeAppGrounds :: Conjurable f => Int -> Int -> String -> f -> [Expr] nonNegativeAppGrounds maxTests maxEvalRecursions nm f = filter (none isNegative . unfoldApp) $ take maxTests $ conjureGrounds f (conjureVarApplication nm f) devlEqual :: Conjurable f => Int -> f -> Defn -> Defn -> Expr -> Bool devlEqual maxEvalRecursions f d1 d2 e = eq `dynApp` evalDyn d1 e `dynApp` evalDyn d2 e `fromDyn` err where evalDyn d e = fromMaybe err (toDynamicWithDefn (conjureExpress f) maxEvalRecursions d e) eq = conjureDynamicEq f err = error "Conjure.devlEqual: evaluation error" -- We cannot use conjureMkEquation here because -- we need different Defns at each side of the equation -- so they have to be evaluated independently. -- | Is the argument value an error value? isError :: a -> Bool isError = isNothing . errorToNothing -- There should not be a problem if this ever appears in LeanCheck: -- imports are qualfied in this module.