{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE UndecidableInstances #-}
module PlutusConformance.Common where
import Control.Monad.Except (runExcept)
import Data.ByteString qualified as BS
import Data.Maybe (fromJust)
import Data.Proxy (Proxy (Proxy))
import Data.Tagged (Tagged (Tagged))
import Data.Text qualified as T
import Data.Text.Encoding qualified as TE
import Data.Text.IO qualified as T
import PlutusCore.Annotation
import PlutusCore.DeBruijn (fakeNameDeBruijn)
import PlutusCore.Default
( DefaultFun
, DefaultUni
)
import PlutusCore.Error (ParserErrorBundle)
import PlutusCore.Evaluation.Machine.CostModelInterface
import PlutusCore.Evaluation.Machine.ExBudget
import PlutusCore.Evaluation.Machine.ExBudgetingDefaults
( defaultCostModelParamsForTesting
)
import PlutusCore.Flat
( DecodeException
, flat
, unflat
)
import PlutusCore.Name.Unique (Name)
import PlutusCore.Quote (runQuoteT)
import PlutusPrelude
( Pretty (pretty)
, display
, void
)
import System.Directory
import System.FilePath
( takeBaseName
, takeFileName
, (<.>)
, (</>)
)
import Test.Tasty
( defaultIngredients
, defaultMainWithIngredients
, includingOptions
, testGroup
)
import Test.Tasty.ExpectedFailure (ignoreTest)
import Test.Tasty.Extras (goldenVsDocM)
import Test.Tasty.Golden (findByExtension)
import Test.Tasty.Golden.Advanced (goldenTest)
import Test.Tasty.Options
( IsOption (..)
, OptionDescription (Option)
, lookupOption
)
import Test.Tasty.Providers (TestTree)
import Test.Tasty.Runners (parseOptions)
import UntypedPlutusCore qualified as UPLC
import UntypedPlutusCore.Parser qualified as UPLC
import Witherable (Witherable (wither))
shownParseError :: T.Text
shownParseError :: Text
shownParseError = Text
"parse/decode error"
shownEvaluationFailure :: T.Text
shownEvaluationFailure :: Text
shownEvaluationFailure = Text
"evaluation failure"
parseTxt
:: T.Text
-> Either ParserErrorBundle (UPLC.Program Name DefaultUni DefaultFun SrcSpan)
parseTxt :: Text
-> Either
ParserErrorBundle (Program Name DefaultUni DefaultFun SrcSpan)
parseTxt Text
resTxt = QuoteT
(Either ParserErrorBundle)
(Program Name DefaultUni DefaultFun SrcSpan)
-> Either
ParserErrorBundle (Program Name DefaultUni DefaultFun SrcSpan)
forall (m :: * -> *) a. Monad m => QuoteT m a -> m a
runQuoteT (QuoteT
(Either ParserErrorBundle)
(Program Name DefaultUni DefaultFun SrcSpan)
-> Either
ParserErrorBundle (Program Name DefaultUni DefaultFun SrcSpan))
-> QuoteT
(Either ParserErrorBundle)
(Program Name DefaultUni DefaultFun SrcSpan)
-> Either
ParserErrorBundle (Program Name DefaultUni DefaultFun SrcSpan)
forall a b. (a -> b) -> a -> b
$ Text
-> QuoteT
(Either ParserErrorBundle)
(Program Name DefaultUni DefaultFun SrcSpan)
forall (m :: * -> *).
(MonadError ParserErrorBundle m, MonadQuote m) =>
Text -> m (Program Name DefaultUni DefaultFun SrcSpan)
UPLC.parseProgram Text
resTxt
type UplcProg = UPLC.Program Name DefaultUni DefaultFun ()
data Format = Textual | Flat
deriving stock (Int -> Format -> ShowS
[Format] -> ShowS
Format -> [Char]
(Int -> Format -> ShowS)
-> (Format -> [Char]) -> ([Format] -> ShowS) -> Show Format
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Format -> ShowS
showsPrec :: Int -> Format -> ShowS
$cshow :: Format -> [Char]
show :: Format -> [Char]
$cshowList :: [Format] -> ShowS
showList :: [Format] -> ShowS
Show, Format -> Format -> Bool
(Format -> Format -> Bool)
-> (Format -> Format -> Bool) -> Eq Format
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Format -> Format -> Bool
== :: Format -> Format -> Bool
$c/= :: Format -> Format -> Bool
/= :: Format -> Format -> Bool
Eq)
formatExtension :: Format -> String
formatExtension :: Format -> [Char]
formatExtension Format
Textual = [Char]
"uplc"
formatExtension Format
Flat = [Char]
"flat"
instance IsOption Format where
defaultValue :: Format
defaultValue = Format
Textual
parseValue :: [Char] -> Maybe Format
parseValue [Char]
s = case [Char]
s of
[Char]
"textual" -> Format -> Maybe Format
forall a. a -> Maybe a
Just Format
Textual
[Char]
"flat" -> Format -> Maybe Format
forall a. a -> Maybe a
Just Format
Flat
[Char]
_ -> Maybe Format
forall a. Maybe a
Nothing
optionName :: Tagged Format [Char]
optionName = [Char] -> Tagged Format [Char]
forall {k} (s :: k) b. b -> Tagged s b
Tagged [Char]
"format"
optionHelp :: Tagged Format [Char]
optionHelp =
[Char] -> Tagged Format [Char]
forall {k} (s :: k) b. b -> Tagged s b
Tagged
[Char]
"The format of the test-case input files to run the tests against: \
\'textual' (textual UPLC source) or 'flat' (flat-encoded UPLC). \
\Default: textual."
data EvaluationResult res = BadMachineParameters | DecodeError | EvalFailure | EvalSuccess res
deriving stock ((forall a b. (a -> b) -> EvaluationResult a -> EvaluationResult b)
-> (forall a b. a -> EvaluationResult b -> EvaluationResult a)
-> Functor EvaluationResult
forall a b. a -> EvaluationResult b -> EvaluationResult a
forall a b. (a -> b) -> EvaluationResult a -> EvaluationResult b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
$cfmap :: forall a b. (a -> b) -> EvaluationResult a -> EvaluationResult b
fmap :: forall a b. (a -> b) -> EvaluationResult a -> EvaluationResult b
$c<$ :: forall a b. a -> EvaluationResult b -> EvaluationResult a
<$ :: forall a b. a -> EvaluationResult b -> EvaluationResult a
Functor)
type UplcEvaluatorFun res = UplcProg -> EvaluationResult res
data UplcEvaluator
=
UplcEvaluatorWithoutCosting (UplcEvaluatorFun UplcProg)
|
UplcEvaluatorWithCosting
(CostModelParams -> UplcEvaluatorFun (UplcProg, ExBudget))
dirsWithNoFlatFiles :: [FilePath]
dirsWithNoFlatFiles :: [[Char]]
dirsWithNoFlatFiles =
[ [Char]
"test-cases/uplc/evaluation/builtin/parser"
, [Char]
"test-cases/uplc/evaluation/term/parser"
]
discoverTests
:: Format
-> UplcEvaluator
-> CostModelParams
-> (FilePath -> Bool)
-> (FilePath -> Bool)
-> FilePath
-> IO TestTree
discoverTests :: Format
-> UplcEvaluator
-> CostModelParams
-> ([Char] -> Bool)
-> ([Char] -> Bool)
-> [Char]
-> IO TestTree
discoverTests Format
fmt UplcEvaluator
eval CostModelParams
modelParams [Char] -> Bool
evaluationFailureExpected [Char] -> Bool
budgetFailureExpected =
Bool -> [Char] -> IO TestTree
go Bool
False
where
ext :: [Char]
ext = Format -> [Char]
formatExtension Format
fmt
go :: Bool -> [Char] -> IO TestTree
go Bool
flatNotExpected [Char]
dir = do
let name :: [Char]
name = ShowS
takeBaseName [Char]
dir
flatNotExpected' :: Bool
flatNotExpected' = Bool
flatNotExpected Bool -> Bool -> Bool
|| [Char]
dir [Char] -> [[Char]] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [[Char]]
dirsWithNoFlatFiles
[[Char]]
children <- [Char] -> IO [[Char]]
listDirectory [Char]
dir
[[Char]]
subdirs <- (([Char] -> IO (Maybe [Char])) -> [[Char]] -> IO [[Char]])
-> [[Char]] -> ([Char] -> IO (Maybe [Char])) -> IO [[Char]]
forall a b c. (a -> b -> c) -> b -> a -> c
flip ([Char] -> IO (Maybe [Char])) -> [[Char]] -> IO [[Char]]
forall (t :: * -> *) (f :: * -> *) a b.
(Witherable t, Applicative f) =>
(a -> f (Maybe b)) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f (Maybe b)) -> [a] -> f [b]
wither [[Char]]
children (([Char] -> IO (Maybe [Char])) -> IO [[Char]])
-> ([Char] -> IO (Maybe [Char])) -> IO [[Char]]
forall a b. (a -> b) -> a -> b
$ \[Char]
child -> do
let fullPath :: [Char]
fullPath = [Char]
dir [Char] -> ShowS
</> [Char]
child
Bool
isDir <- [Char] -> IO Bool
doesDirectoryExist [Char]
fullPath
Maybe [Char] -> IO (Maybe [Char])
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe [Char] -> IO (Maybe [Char]))
-> Maybe [Char] -> IO (Maybe [Char])
forall a b. (a -> b) -> a -> b
$ if Bool
isDir then [Char] -> Maybe [Char]
forall a. a -> Maybe a
Just [Char]
fullPath else Maybe [Char]
forall a. Maybe a
Nothing
if [[Char]] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [[Char]]
subdirs
then do
[[Char]]
inputFiles <- [[Char]] -> [Char] -> IO [[Char]]
findByExtension [[Char]
"." [Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> [Char]
ext] [Char]
dir
let expectedInputFile :: [Char]
expectedInputFile = ShowS
takeFileName [Char]
dir [Char] -> ShowS
<.> [Char]
ext
case [[Char]]
inputFiles of
[] ->
if Format
fmt Format -> Format -> Bool
forall a. Eq a => a -> a -> Bool
== Format
Flat Bool -> Bool -> Bool
&& Bool
flatNotExpected'
then
TestTree -> IO TestTree
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (TestTree -> IO TestTree) -> TestTree -> IO TestTree
forall a b. (a -> b) -> a -> b
$ [Char] -> [TestTree] -> TestTree
testGroup [Char]
name []
else [Char] -> IO TestTree
forall a. HasCallStack => [Char] -> a
error ([Char] -> IO TestTree) -> [Char] -> IO TestTree
forall a b. (a -> b) -> a -> b
$ [Char]
"Input file " [Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ [Char]
expectedInputFile [Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ [Char]
" missing in " [Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ [Char]
dir
[Char]
_ : [Char]
_ : [[Char]]
_ -> [Char] -> IO TestTree
forall a. HasCallStack => [Char] -> a
error ([Char] -> IO TestTree) -> [Char] -> IO TestTree
forall a b. (a -> b) -> a -> b
$ [Char]
"More than one ." [Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> [Char]
ext [Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> [Char]
" file in " [Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> [Char]
dir
[[Char]
inputFilePath] ->
if ShowS
takeFileName [Char]
inputFilePath [Char] -> [Char] -> Bool
forall a. Eq a => a -> a -> Bool
/= [Char]
expectedInputFile
then
[Char] -> IO TestTree
forall a. HasCallStack => [Char] -> a
error ([Char] -> IO TestTree) -> [Char] -> IO TestTree
forall a b. (a -> b) -> a -> b
$
[Char]
"Found file "
[Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ ShowS
takeFileName [Char]
inputFilePath
[Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ [Char]
" in directory "
[Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ [Char]
dir
[Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ [Char]
" (expected "
[Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ [Char]
expectedInputFile
[Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ [Char]
")"
else TestTree -> IO TestTree
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (TestTree -> IO TestTree) -> TestTree -> IO TestTree
forall a b. (a -> b) -> a -> b
$ case UplcEvaluator
eval of
UplcEvaluatorWithCosting CostModelParams -> UplcEvaluatorFun (UplcProg, ExBudget)
f ->
[Char] -> [TestTree] -> TestTree
testGroup
[Char]
name
[ [Char] -> [Char] -> UplcEvaluatorFun UplcProg -> TestTree
testForEval [Char]
dir [Char]
inputFilePath (((UplcProg, ExBudget) -> UplcProg)
-> EvaluationResult (UplcProg, ExBudget)
-> EvaluationResult UplcProg
forall a b. (a -> b) -> EvaluationResult a -> EvaluationResult b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (UplcProg, ExBudget) -> UplcProg
forall a b. (a, b) -> a
fst (EvaluationResult (UplcProg, ExBudget)
-> EvaluationResult UplcProg)
-> UplcEvaluatorFun (UplcProg, ExBudget)
-> UplcEvaluatorFun UplcProg
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CostModelParams -> UplcEvaluatorFun (UplcProg, ExBudget)
f CostModelParams
modelParams)
, [Char] -> [Char] -> UplcEvaluatorFun ExBudget -> TestTree
testForBudget [Char]
dir [Char]
inputFilePath (((UplcProg, ExBudget) -> ExBudget)
-> EvaluationResult (UplcProg, ExBudget)
-> EvaluationResult ExBudget
forall a b. (a -> b) -> EvaluationResult a -> EvaluationResult b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (UplcProg, ExBudget) -> ExBudget
forall a b. (a, b) -> b
snd (EvaluationResult (UplcProg, ExBudget)
-> EvaluationResult ExBudget)
-> UplcEvaluatorFun (UplcProg, ExBudget)
-> UplcEvaluatorFun ExBudget
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CostModelParams -> UplcEvaluatorFun (UplcProg, ExBudget)
f CostModelParams
modelParams)
]
UplcEvaluatorWithoutCosting UplcEvaluatorFun UplcProg
f -> [Char] -> [Char] -> UplcEvaluatorFun UplcProg -> TestTree
testForEval [Char]
dir [Char]
inputFilePath UplcEvaluatorFun UplcProg
f
else [Char] -> [TestTree] -> TestTree
testGroup [Char]
name ([TestTree] -> TestTree) -> IO [TestTree] -> IO TestTree
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ([Char] -> IO TestTree) -> [[Char]] -> IO [TestTree]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> [a] -> f [b]
traverse (Bool -> [Char] -> IO TestTree
go Bool
flatNotExpected') [[Char]]
subdirs
goldenBasePath :: ShowS
goldenBasePath [Char]
dir = [Char]
dir [Char] -> ShowS
</> ShowS
takeBaseName [Char]
dir
testForEval :: FilePath -> FilePath -> UplcEvaluatorFun UplcProg -> TestTree
testForEval :: [Char] -> [Char] -> UplcEvaluatorFun UplcProg -> TestTree
testForEval [Char]
dir [Char]
inputFilePath UplcEvaluatorFun UplcProg
e =
let goldenFilePath :: [Char]
goldenFilePath = ShowS
goldenBasePath [Char]
dir [Char] -> ShowS
<.> [Char]
ext [Char] -> ShowS
<.> [Char]
"expected"
test :: TestTree
test =
[Char]
-> IO (Either Text UplcProg)
-> IO (Either Text UplcProg)
-> (Either Text UplcProg
-> Either Text UplcProg -> IO (Maybe [Char]))
-> (Either Text UplcProg -> IO ())
-> TestTree
forall a.
[Char]
-> IO a
-> IO a
-> (a -> a -> IO (Maybe [Char]))
-> (a -> IO ())
-> TestTree
goldenTest
(ShowS
takeFileName [Char]
inputFilePath [Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ [Char]
" (evaluation)")
(Format -> [Char] -> IO (Either Text UplcProg)
getExpectedProg Format
fmt [Char]
goldenFilePath)
(Format
-> UplcEvaluatorFun UplcProg -> [Char] -> IO (Either Text UplcProg)
forall res.
Format -> UplcEvaluatorFun res -> [Char] -> IO (Either Text res)
getTestedValue Format
fmt UplcEvaluatorFun UplcProg
e [Char]
inputFilePath)
(\Either Text UplcProg
x Either Text UplcProg
y -> Maybe [Char] -> IO (Maybe [Char])
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe [Char] -> IO (Maybe [Char]))
-> Maybe [Char] -> IO (Maybe [Char])
forall a b. (a -> b) -> a -> b
$ Either Text UplcProg -> Either Text UplcProg -> Maybe [Char]
compareAlphaEq Either Text UplcProg
x Either Text UplcProg
y)
(Format -> [Char] -> Either Text UplcProg -> IO ()
updateGoldenFile Format
fmt [Char]
goldenFilePath)
in Bool -> TestTree -> TestTree
possiblyFailingTest ([Char] -> Bool
evaluationFailureExpected [Char]
dir) TestTree
test
testForBudget :: FilePath -> FilePath -> UplcEvaluatorFun ExBudget -> TestTree
testForBudget :: [Char] -> [Char] -> UplcEvaluatorFun ExBudget -> TestTree
testForBudget [Char]
dir [Char]
inputFilePath UplcEvaluatorFun ExBudget
e =
let goldenFilePath :: [Char]
goldenFilePath = ShowS
goldenBasePath [Char]
dir [Char] -> ShowS
<.> [Char]
"budget" [Char] -> ShowS
<.> [Char]
"expected"
prettyEither :: Either a a -> Doc ann
prettyEither (Left a
l) = a -> Doc ann
forall ann. a -> Doc ann
forall a ann. Pretty a => a -> Doc ann
pretty a
l
prettyEither (Right a
r) = a -> Doc ann
forall ann. a -> Doc ann
forall a ann. Pretty a => a -> Doc ann
pretty a
r
test :: TestTree
test =
[Char] -> [Char] -> IO (Doc Any) -> TestTree
forall ann. [Char] -> [Char] -> IO (Doc ann) -> TestTree
goldenVsDocM
(ShowS
takeFileName [Char]
inputFilePath [Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ [Char]
" (budget)")
[Char]
goldenFilePath
(Either Text ExBudget -> Doc Any
forall {a} {a} {ann}. (Pretty a, Pretty a) => Either a a -> Doc ann
prettyEither (Either Text ExBudget -> Doc Any)
-> IO (Either Text ExBudget) -> IO (Doc Any)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Format
-> UplcEvaluatorFun ExBudget -> [Char] -> IO (Either Text ExBudget)
forall res.
Format -> UplcEvaluatorFun res -> [Char] -> IO (Either Text res)
getTestedValue Format
fmt UplcEvaluatorFun ExBudget
e [Char]
inputFilePath)
in Bool -> TestTree -> TestTree
possiblyFailingTest ([Char] -> Bool
budgetFailureExpected [Char]
dir) TestTree
test
possiblyFailingTest :: Bool -> TestTree -> TestTree
possiblyFailingTest :: Bool -> TestTree -> TestTree
possiblyFailingTest Bool
failureExpected TestTree
test =
if Bool
failureExpected
then TestTree -> TestTree
ignoreTest TestTree
test
else TestTree
test
looksLikeUplcProgram :: T.Text -> Bool
looksLikeUplcProgram :: Text -> Bool
looksLikeUplcProgram Text
t =
case Text -> Maybe (Char, Text)
T.uncons (Text -> Text
dropLeadingCommentsAndSpace Text
t) of
Just (Char
'(', Text
rest) -> Text
"program" Text -> Text -> Bool
`T.isPrefixOf` Text -> Text
dropLeadingCommentsAndSpace Text
rest
Maybe (Char, Text)
_ -> Bool
False
where
dropLeadingCommentsAndSpace :: T.Text -> T.Text
dropLeadingCommentsAndSpace :: Text -> Text
dropLeadingCommentsAndSpace Text
s =
let s' :: Text
s' = Text -> Text
T.stripStart Text
s
in if Text
"--" Text -> Text -> Bool
`T.isPrefixOf` Text
s'
then Text -> Text
dropLeadingCommentsAndSpace ((Char -> Bool) -> Text -> Text
T.dropWhile (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'\n') Text
s')
else case Text -> Text -> Maybe Text
T.stripPrefix Text
"{-" Text
s' of
Just Text
rest -> Text -> Text
dropLeadingCommentsAndSpace (Int -> Text -> Text
dropBlockComment Int
1 Text
rest)
Maybe Text
Nothing -> Text
s'
dropBlockComment :: Int -> T.Text -> T.Text
dropBlockComment :: Int -> Text -> Text
dropBlockComment Int
0 Text
s = Text
s
dropBlockComment Int
depth Text
s
| Just Text
rest <- Text -> Text -> Maybe Text
T.stripPrefix Text
"{-" Text
s = Int -> Text -> Text
dropBlockComment (Int
depth Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) Text
rest
| Just Text
rest <- Text -> Text -> Maybe Text
T.stripPrefix Text
"-}" Text
s = Int -> Text -> Text
dropBlockComment (Int
depth Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) Text
rest
| Just (Char
_, Text
rest) <- Text -> Maybe (Char, Text)
T.uncons Text
s = Int -> Text -> Text
dropBlockComment Int
depth Text
rest
| Bool
otherwise = Text
s
expectedToProg :: T.Text -> Either T.Text UplcProg
expectedToProg :: Text -> Either Text UplcProg
expectedToProg Text
txt
| Bool -> Bool
not (Text -> Bool
looksLikeUplcProgram Text
txt) = Text -> Either Text UplcProg
forall a b. a -> Either a b
Left Text
txt
| Bool
otherwise =
case Text
-> Either
ParserErrorBundle (Program Name DefaultUni DefaultFun SrcSpan)
parseTxt Text
txt of
Left ParserErrorBundle
_ -> Text -> Either Text UplcProg
forall a b. a -> Either a b
Left Text
txt
Right Program Name DefaultUni DefaultFun SrcSpan
p -> UplcProg -> Either Text UplcProg
forall a b. b -> Either a b
Right (UplcProg -> Either Text UplcProg)
-> UplcProg -> Either Text UplcProg
forall a b. (a -> b) -> a -> b
$ Program Name DefaultUni DefaultFun SrcSpan -> UplcProg
forall (f :: * -> *) a. Functor f => f a -> f ()
void Program Name DefaultUni DefaultFun SrcSpan
p
decodeFlatExpected :: BS.ByteString -> Either T.Text UplcProg
decodeFlatExpected :: ByteString -> Either Text UplcProg
decodeFlatExpected ByteString
input =
case ByteString -> Either UnicodeException Text
TE.decodeUtf8' ByteString
input of
Right Text
txt
| Text
txt Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
shownParseError Bool -> Bool -> Bool
|| Text
txt Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
shownEvaluationFailure -> Text -> Either Text UplcProg
forall a b. a -> Either a b
Left Text
txt
Either UnicodeException Text
_ -> case ByteString -> Either [Char] UplcProg
decodeFlatProg ByteString
input of
Right UplcProg
p -> UplcProg -> Either Text UplcProg
forall a b. b -> Either a b
Right UplcProg
p
Left [Char]
err -> Text -> Either Text UplcProg
forall a b. a -> Either a b
Left (Text -> Either Text UplcProg) -> Text -> Either Text UplcProg
forall a b. (a -> b) -> a -> b
$ [Char] -> Text
T.pack [Char]
err
getExpectedProg :: Format -> FilePath -> IO (Either T.Text UplcProg)
getExpectedProg :: Format -> [Char] -> IO (Either Text UplcProg)
getExpectedProg Format
Textual [Char]
file = Text -> Either Text UplcProg
expectedToProg (Text -> Either Text UplcProg)
-> IO Text -> IO (Either Text UplcProg)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Char] -> IO Text
T.readFile [Char]
file
getExpectedProg Format
Flat [Char]
file = ByteString -> Either Text UplcProg
decodeFlatExpected (ByteString -> Either Text UplcProg)
-> IO ByteString -> IO (Either Text UplcProg)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Char] -> IO ByteString
BS.readFile [Char]
file
getInputProg :: Format -> FilePath -> IO (Either T.Text UplcProg)
getInputProg :: Format -> [Char] -> IO (Either Text UplcProg)
getInputProg Format
Textual [Char]
file = do
Text
input <- [Char] -> IO Text
T.readFile [Char]
file
Either Text UplcProg -> IO (Either Text UplcProg)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either Text UplcProg -> IO (Either Text UplcProg))
-> Either Text UplcProg -> IO (Either Text UplcProg)
forall a b. (a -> b) -> a -> b
$
if Text -> Bool
looksLikeUplcProgram Text
input
then case Text
-> Either
ParserErrorBundle (Program Name DefaultUni DefaultFun SrcSpan)
parseTxt Text
input of
Left ParserErrorBundle
_ -> Text -> Either Text UplcProg
forall a b. a -> Either a b
Left Text
shownParseError
Right Program Name DefaultUni DefaultFun SrcSpan
p -> UplcProg -> Either Text UplcProg
forall a b. b -> Either a b
Right (UplcProg -> Either Text UplcProg)
-> UplcProg -> Either Text UplcProg
forall a b. (a -> b) -> a -> b
$ Program Name DefaultUni DefaultFun SrcSpan -> UplcProg
forall (f :: * -> *) a. Functor f => f a -> f ()
void Program Name DefaultUni DefaultFun SrcSpan
p
else Text -> Either Text UplcProg
forall a b. a -> Either a b
Left Text
shownParseError
getInputProg Format
Flat [Char]
file = do
ByteString
input <- [Char] -> IO ByteString
BS.readFile [Char]
file
Either Text UplcProg -> IO (Either Text UplcProg)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either Text UplcProg -> IO (Either Text UplcProg))
-> Either Text UplcProg -> IO (Either Text UplcProg)
forall a b. (a -> b) -> a -> b
$
if ByteString -> Bool
BS.null ByteString
input
then Text -> Either Text UplcProg
forall a b. a -> Either a b
Left Text
shownParseError
else case ByteString -> Either [Char] UplcProg
decodeFlatProg ByteString
input of
Left [Char]
_ -> Text -> Either Text UplcProg
forall a b. a -> Either a b
Left Text
shownParseError
Right UplcProg
p -> UplcProg -> Either Text UplcProg
forall a b. b -> Either a b
Right UplcProg
p
getTestedValue
:: Format
-> UplcEvaluatorFun res
-> FilePath
-> IO (Either T.Text res)
getTestedValue :: forall res.
Format -> UplcEvaluatorFun res -> [Char] -> IO (Either Text res)
getTestedValue Format
fmt UplcEvaluatorFun res
eval [Char]
file = do
Either Text UplcProg
inputProg <- Format -> [Char] -> IO (Either Text UplcProg)
getInputProg Format
fmt [Char]
file
Either Text res -> IO (Either Text res)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either Text res -> IO (Either Text res))
-> Either Text res -> IO (Either Text res)
forall a b. (a -> b) -> a -> b
$ case Either Text UplcProg
inputProg of
Left Text
err -> Text -> Either Text res
forall a b. a -> Either a b
Left Text
err
Right UplcProg
p ->
case UplcEvaluatorFun res
eval UplcProg
p of
EvaluationResult res
BadMachineParameters -> Text -> Either Text res
forall a b. a -> Either a b
Left Text
shownEvaluationFailure
EvaluationResult res
DecodeError -> Text -> Either Text res
forall a b. a -> Either a b
Left Text
shownParseError
EvaluationResult res
EvalFailure -> Text -> Either Text res
forall a b. a -> Either a b
Left Text
shownEvaluationFailure
EvalSuccess res
prog -> res -> Either Text res
forall a b. b -> Either a b
Right res
prog
compareAlphaEq
:: Either T.Text UplcProg
-> Either T.Text UplcProg
-> Maybe String
compareAlphaEq :: Either Text UplcProg -> Either Text UplcProg -> Maybe [Char]
compareAlphaEq (Left Text
expectedTxt) (Left Text
actualTxt) =
if Text
actualTxt Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
expectedTxt
then Maybe [Char]
forall a. Maybe a
Nothing
else
[Char] -> Maybe [Char]
forall a. a -> Maybe a
Just ([Char] -> Maybe [Char]) -> [Char] -> Maybe [Char]
forall a b. (a -> b) -> a -> b
$
[Char]
"Test failed, the output failed to parse or evaluate: \n"
[Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> Text -> [Char]
T.unpack Text
actualTxt
compareAlphaEq (Right UplcProg
expected) (Right UplcProg
actual) =
if UplcProg
actual UplcProg -> UplcProg -> Bool
forall a. Eq a => a -> a -> Bool
== UplcProg
expected
then Maybe [Char]
forall a. Maybe a
Nothing
else
[Char] -> Maybe [Char]
forall a. a -> Maybe a
Just ([Char] -> Maybe [Char]) -> [Char] -> Maybe [Char]
forall a b. (a -> b) -> a -> b
$
[Char]
"Test failed, the output was successfully parsed and evaluated, \
\but it isn't as expected. "
[Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> [Char]
"The output program is: \n"
[Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> UplcProg -> [Char]
forall str a. (Pretty a, Render str) => a -> str
display UplcProg
actual
[Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> [Char]
"\n The output program, with the unique names shown is: \n"
[Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> UplcProg -> [Char]
forall a. Show a => a -> [Char]
show UplcProg
actual
[Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> [Char]
"\n But the expected result, with the unique names shown is: \n"
[Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> UplcProg -> [Char]
forall a. Show a => a -> [Char]
show UplcProg
expected
compareAlphaEq (Right UplcProg
expected) (Left Text
actualTxt) =
[Char] -> Maybe [Char]
forall a. a -> Maybe a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([Char] -> Maybe [Char]) -> [Char] -> Maybe [Char]
forall a b. (a -> b) -> a -> b
$
[Char]
"Test failed, the output failed to parse or evaluate: \n"
[Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> Text -> [Char]
T.unpack Text
actualTxt
[Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> [Char]
"\n But the expected result, with the unique names shown is: \n"
[Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> UplcProg -> [Char]
forall a. Show a => a -> [Char]
show UplcProg
expected
compareAlphaEq (Left Text
txt) (Right UplcProg
actual) =
if Text
txt Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== UplcProg -> Text
forall str a. (Pretty a, Render str) => a -> str
display UplcProg
actual
then Maybe [Char]
forall a. Maybe a
Nothing
else
[Char] -> Maybe [Char]
forall a. a -> Maybe a
Just ([Char] -> Maybe [Char]) -> [Char] -> Maybe [Char]
forall a b. (a -> b) -> a -> b
$
[Char]
"Test failed, the output was successfully parsed and evaluated, \
\but it isn't as expected. "
[Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> [Char]
"The output program is: "
[Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> UplcProg -> [Char]
forall str a. (Pretty a, Render str) => a -> str
display UplcProg
actual
[Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> [Char]
". But the expected result is: "
[Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> Text -> [Char]
T.unpack Text
txt
updateGoldenFile
:: Format
-> FilePath
-> Either T.Text UplcProg
-> IO ()
updateGoldenFile :: Format -> [Char] -> Either Text UplcProg -> IO ()
updateGoldenFile Format
Textual [Char]
goldenPath (Left Text
txt) = [Char] -> Text -> IO ()
T.writeFile [Char]
goldenPath Text
txt
updateGoldenFile Format
Textual [Char]
goldenPath (Right UplcProg
p) = [Char] -> Text -> IO ()
T.writeFile [Char]
goldenPath (UplcProg -> Text
forall str a. (Pretty a, Render str) => a -> str
display UplcProg
p)
updateGoldenFile Format
Flat [Char]
goldenPath (Left Text
txt) = [Char] -> ByteString -> IO ()
BS.writeFile [Char]
goldenPath (Text -> ByteString
TE.encodeUtf8 Text
txt)
updateGoldenFile Format
Flat [Char]
goldenPath (Right UplcProg
p) = [Char] -> ByteString -> IO ()
BS.writeFile [Char]
goldenPath (UplcProg -> ByteString
encodeFlatProg UplcProg
p)
representativeGoldenTest :: TestTree
representativeGoldenTest :: TestTree
representativeGoldenTest =
[Char]
-> IO ()
-> IO ()
-> (() -> () -> IO (Maybe [Char]))
-> (() -> IO ())
-> TestTree
forall a.
[Char]
-> IO a
-> IO a
-> (a -> a -> IO (Maybe [Char]))
-> (a -> IO ())
-> TestTree
goldenTest
[Char]
"representative golden test (for option discovery only)"
(() -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ())
(() -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ())
(\()
_ ()
_ -> Maybe [Char] -> IO (Maybe [Char])
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe [Char]
forall a. Maybe a
Nothing)
(\()
_ -> () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ())
runUplcEvalTests
:: UplcEvaluator
-> (FilePath -> Bool)
-> (FilePath -> Bool)
-> IO ()
runUplcEvalTests :: UplcEvaluator -> ([Char] -> Bool) -> ([Char] -> Bool) -> IO ()
runUplcEvalTests UplcEvaluator
eval [Char] -> Bool
expectedFailTests [Char] -> Bool
expectedBudgetFailTests = do
let params :: CostModelParams
params = Maybe CostModelParams -> CostModelParams
forall a. HasCallStack => Maybe a -> a
fromJust Maybe CostModelParams
defaultCostModelParamsForTesting
ingredients :: [Ingredient]
ingredients = [OptionDescription] -> Ingredient
includingOptions [Proxy Format -> OptionDescription
forall v. IsOption v => Proxy v -> OptionDescription
Option (Proxy Format
forall {k} (t :: k). Proxy t
Proxy :: Proxy Format)] Ingredient -> [Ingredient] -> [Ingredient]
forall a. a -> [a] -> [a]
: [Ingredient]
defaultIngredients
OptionSet
opts <- [Ingredient] -> TestTree -> IO OptionSet
parseOptions [Ingredient]
ingredients ([Char] -> [TestTree] -> TestTree
testGroup [Char]
"" [TestTree
representativeGoldenTest])
let fmt :: Format
fmt = OptionSet -> Format
forall v. IsOption v => OptionSet -> v
lookupOption OptionSet
opts :: Format
TestTree
tests <-
Format
-> UplcEvaluator
-> CostModelParams
-> ([Char] -> Bool)
-> ([Char] -> Bool)
-> [Char]
-> IO TestTree
discoverTests
Format
fmt
UplcEvaluator
eval
CostModelParams
params
[Char] -> Bool
expectedFailTests
[Char] -> Bool
expectedBudgetFailTests
[Char]
"test-cases/uplc/evaluation"
[Ingredient] -> TestTree -> IO ()
defaultMainWithIngredients [Ingredient]
ingredients (TestTree -> IO ()) -> TestTree -> IO ()
forall a b. (a -> b) -> a -> b
$ [Char] -> [TestTree] -> TestTree
testGroup [Char]
"UPLC evaluation tests" [TestTree
tests]
unDeBruijnProgram
:: UPLC.Program UPLC.NamedDeBruijn DefaultUni DefaultFun ()
-> Either UPLC.FreeVariableError UplcProg
unDeBruijnProgram :: Program NamedDeBruijn DefaultUni DefaultFun ()
-> Either FreeVariableError UplcProg
unDeBruijnProgram (UPLC.Program ()
ann Version
ver Term NamedDeBruijn DefaultUni DefaultFun ()
t) =
QuoteT (Either FreeVariableError) UplcProg
-> Either FreeVariableError UplcProg
forall (m :: * -> *) a. Monad m => QuoteT m a -> m a
runQuoteT (() -> Version -> Term Name DefaultUni DefaultFun () -> UplcProg
forall name (uni :: * -> *) fun ann.
ann -> Version -> Term name uni fun ann -> Program name uni fun ann
UPLC.Program ()
ann Version
ver (Term Name DefaultUni DefaultFun () -> UplcProg)
-> QuoteT
(Either FreeVariableError) (Term Name DefaultUni DefaultFun ())
-> QuoteT (Either FreeVariableError) UplcProg
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Term NamedDeBruijn DefaultUni DefaultFun ()
-> QuoteT
(Either FreeVariableError) (Term Name DefaultUni DefaultFun ())
forall (m :: * -> *) (uni :: * -> *) fun ann.
(MonadQuote m, MonadError FreeVariableError m) =>
Term NamedDeBruijn uni fun ann -> m (Term Name uni fun ann)
UPLC.unDeBruijnTerm Term NamedDeBruijn DefaultUni DefaultFun ()
t)
decodeFlatProg :: BS.ByteString -> Either String UplcProg
decodeFlatProg :: ByteString -> Either [Char] UplcProg
decodeFlatProg ByteString
bs =
case Either
DecodeException
(UnrestrictedProgram DeBruijn DefaultUni DefaultFun ())
decoded of
Left DecodeException
err -> [Char] -> Either [Char] UplcProg
forall a b. a -> Either a b
Left ([Char] -> Either [Char] UplcProg)
-> [Char] -> Either [Char] UplcProg
forall a b. (a -> b) -> a -> b
$ DecodeException -> [Char]
forall a. Show a => a -> [Char]
show DecodeException
err
Right (UPLC.UnrestrictedProgram Program DeBruijn DefaultUni DefaultFun ()
dbProg) ->
case Program NamedDeBruijn DefaultUni DefaultFun ()
-> Either FreeVariableError UplcProg
unDeBruijnProgram ((DeBruijn -> NamedDeBruijn)
-> Program DeBruijn DefaultUni DefaultFun ()
-> Program NamedDeBruijn DefaultUni DefaultFun ()
forall name name' (uni :: * -> *) fun ann.
(name -> name')
-> Program name uni fun ann -> Program name' uni fun ann
UPLC.programMapNames DeBruijn -> NamedDeBruijn
fakeNameDeBruijn Program DeBruijn DefaultUni DefaultFun ()
dbProg) of
Left FreeVariableError
err -> [Char] -> Either [Char] UplcProg
forall a b. a -> Either a b
Left ([Char] -> Either [Char] UplcProg)
-> [Char] -> Either [Char] UplcProg
forall a b. (a -> b) -> a -> b
$ FreeVariableError -> [Char]
forall a. Show a => a -> [Char]
show FreeVariableError
err
Right UplcProg
prog -> UplcProg -> Either [Char] UplcProg
forall a b. b -> Either a b
Right UplcProg
prog
where
decoded
:: Either
DecodeException
(UPLC.UnrestrictedProgram UPLC.DeBruijn DefaultUni DefaultFun ())
decoded :: Either
DecodeException
(UnrestrictedProgram DeBruijn DefaultUni DefaultFun ())
decoded = ByteString
-> Either
DecodeException
(UnrestrictedProgram DeBruijn DefaultUni DefaultFun ())
forall a b. (Flat a, AsByteString b) => b -> Decoded a
unflat ByteString
bs
encodeFlatProg :: UplcProg -> BS.ByteString
encodeFlatProg :: UplcProg -> ByteString
encodeFlatProg (UPLC.Program ()
ann Version
ver Term Name DefaultUni DefaultFun ()
t) =
case Except
FreeVariableError (Term NamedDeBruijn DefaultUni DefaultFun ())
-> Either
FreeVariableError (Term NamedDeBruijn DefaultUni DefaultFun ())
forall e a. Except e a -> Either e a
runExcept (Term Name DefaultUni DefaultFun ()
-> Except
FreeVariableError (Term NamedDeBruijn DefaultUni DefaultFun ())
forall (m :: * -> *) (uni :: * -> *) fun ann.
MonadError FreeVariableError m =>
Term Name uni fun ann -> m (Term NamedDeBruijn uni fun ann)
UPLC.deBruijnTerm Term Name DefaultUni DefaultFun ()
t) of
Left (FreeVariableError
err :: UPLC.FreeVariableError) -> [Char] -> ByteString
forall a. HasCallStack => [Char] -> a
error ([Char] -> ByteString) -> [Char] -> ByteString
forall a b. (a -> b) -> a -> b
$ [Char]
"encodeFlatProg (deBruijnTerm): " [Char] -> ShowS
forall a. Semigroup a => a -> a -> a
<> FreeVariableError -> [Char]
forall a. Show a => a -> [Char]
show FreeVariableError
err
Right Term NamedDeBruijn DefaultUni DefaultFun ()
namedDbTerm ->
UnrestrictedProgram DeBruijn DefaultUni DefaultFun () -> ByteString
forall a. Flat a => a -> ByteString
flat (UnrestrictedProgram DeBruijn DefaultUni DefaultFun ()
-> ByteString)
-> UnrestrictedProgram DeBruijn DefaultUni DefaultFun ()
-> ByteString
forall a b. (a -> b) -> a -> b
$
Program DeBruijn DefaultUni DefaultFun ()
-> UnrestrictedProgram DeBruijn DefaultUni DefaultFun ()
forall name (uni :: * -> *) fun ann.
Program name uni fun ann -> UnrestrictedProgram name uni fun ann
UPLC.UnrestrictedProgram (Program DeBruijn DefaultUni DefaultFun ()
-> UnrestrictedProgram DeBruijn DefaultUni DefaultFun ())
-> Program DeBruijn DefaultUni DefaultFun ()
-> UnrestrictedProgram DeBruijn DefaultUni DefaultFun ()
forall a b. (a -> b) -> a -> b
$
(NamedDeBruijn -> DeBruijn)
-> Program NamedDeBruijn DefaultUni DefaultFun ()
-> Program DeBruijn DefaultUni DefaultFun ()
forall name name' (uni :: * -> *) fun ann.
(name -> name')
-> Program name uni fun ann -> Program name' uni fun ann
UPLC.programMapNames NamedDeBruijn -> DeBruijn
UPLC.unNameDeBruijn (()
-> Version
-> Term NamedDeBruijn DefaultUni DefaultFun ()
-> Program NamedDeBruijn DefaultUni DefaultFun ()
forall name (uni :: * -> *) fun ann.
ann -> Version -> Term name uni fun ann -> Program name uni fun ann
UPLC.Program ()
ann Version
ver Term NamedDeBruijn DefaultUni DefaultFun ()
namedDbTerm)