Skip to content

Commit

Permalink
Add test suite for REPL expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
paulcadman committed Feb 12, 2024
1 parent 67baec3 commit 51187dd
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 64 deletions.
4 changes: 3 additions & 1 deletion test/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import Nockma qualified
import Package qualified
import Parsing qualified
import Reg qualified
import Repl qualified
import Resolver qualified
import Runtime qualified
import Scope qualified
Expand All @@ -37,7 +38,8 @@ slowTests =
Compilation.allTests,
Examples.allTests,
Casm.allTests,
VampIR.allTests
VampIR.allTests,
Repl.allTests
]

fastTests :: TestTree
Expand Down
49 changes: 49 additions & 0 deletions test/Repl/Assertions.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
module Repl.Assertions where

import Base
import Juvix.Compiler.Core qualified as Core
import Juvix.Compiler.Core.Extra.Value qualified as Core
import Juvix.Compiler.Core.Language.Value qualified as Core
import Juvix.Compiler.Core.Pretty qualified as Core

assertNoJuvixError :: Either JuvixError a -> IO a
assertNoJuvixError = either (assertFailure . ("JuvixError: " <>) . unpack . renderTextDefault) return

assertPrettyCodeEqual :: (Core.PrettyCode a) => (a -> a -> Bool) -> a -> a -> Assertion
assertPrettyCodeEqual eq expected actual = unless (eq expected actual) (assertFailure (unpack msg))
where
msg :: Text
msg = "expected: " <> Core.ppTrace expected <> "\n but got: " <> Core.ppTrace actual

assertNodeEqual :: Core.Node -> Core.Node -> Assertion
assertNodeEqual = assertPrettyCodeEqual (==)

assertValueEqual :: Core.Value -> Core.Value -> Assertion
assertValueEqual = assertPrettyCodeEqual valueEq

valueEqList :: [Core.Value] -> [Core.Value] -> Bool
valueEqList vs1 vs2 = case (vs1, vs2) of
([], []) -> True
([], _) -> False
(_, []) -> False
(x : xs, y : ys) -> valueEq x y && valueEqList xs ys

valueEq :: Core.Value -> Core.Value -> Bool
valueEq n = \case
Core.ValueConstrApp app1 -> case n of
Core.ValueConstrApp app2 ->
app1 ^. Core.constrAppName == app2 ^. Core.constrAppName
&& valueEqList (app1 ^. Core.constrAppArgs) (app2 ^. Core.constrAppArgs)
_ -> False
Core.ValueConstant v1 -> case n of
Core.ValueConstant v2 -> v1 == v2
_ -> False
Core.ValueWildcard -> case n of
Core.ValueWildcard -> True
_ -> False
Core.ValueFun -> case n of
Core.ValueFun -> True
_ -> False
Core.ValueType -> case n of
Core.ValueType -> True
_ -> False
138 changes: 105 additions & 33 deletions test/Repl/Positive.hs
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
module Repl.Positive where

import Base
import Juvix.Data.Effect.TaggedLock
import Juvix.Extra.Stdlib
import Juvix.Compiler.Pipeline.Root
import Juvix.Compiler.Core qualified as Core
import Juvix.Compiler.Core.Extra.Value qualified as Core
import Juvix.Compiler.Core.Language.Value qualified as Core
import Juvix.Compiler.Core.Transformation
import Juvix.Compiler.Pipeline.Repl
import Juvix.Compiler.Pipeline.Root
import Juvix.Data.Effect.TaggedLock
import Juvix.Extra.Paths qualified as P
import Juvix.Extra.Stdlib
import Juvix.Extra.Strings qualified as Str
import Repl.Assertions

runTaggedLockIO' :: Sem '[TaggedLock, Files, Embed IO, Resource, Final IO] a -> IO a
runTaggedLockIO' = runFinal
. resourceToIOFinal
. embedToFinal @IO
. runFilesIO
. runTaggedLock LockModePermissive
runTaggedLockIO' :: Sem '[Files, TaggedLock, Embed IO] a -> IO a
runTaggedLockIO' =
runM
. runTaggedLockPermissive
. runFilesIO

loadPrelude :: Path Abs Dir -> IO (Artifacts, EntryPoint)
loadPrelude rootDir = runTaggedLockIO' $ do
Expand All @@ -22,24 +26,33 @@ loadPrelude rootDir = runTaggedLockIO' $ do
let ep = defaultEntryPoint pkg root (rootDir <//> preludePath)
artif <- embed (runReplPipelineIO ep)
return (artif, ep)

where
root :: Root
root = Root {_rootRootDir=rootDir,
_rootPackageType=LocalPackage,
_rootInvokeDir=rootDir,
_rootBuildDir=DefaultBuildDir}
root =
Root
{ _rootRootDir = rootDir,
_rootPackageType = LocalPackage,
_rootInvokeDir = rootDir,
_rootBuildDir = DefaultBuildDir
}

data TestCtx = TestCtx
{ _testCtxRootDir :: Path Abs Dir,
_testCtxEntryPoint :: EntryPoint,
_testCtxArtifacts :: Artifacts
}

data TestCtx = TestCtx {
_testCtxRootDir :: Path Abs Dir,
_testCtxEntryPoint :: EntryPoint,
_testCtxArtifacts :: Artifacts
}
data PosTest = PosTest
{ _posTestName :: Text,
_posTestInput :: Text,
_posTestExpected :: Core.Value
}

makeLenses ''TestCtx
makeLenses ''PosTest

assertNodeEqual :: Core.Node -> Core.Node -> Assertion
assertNodeEqual n1 n2 = undefined
mkPreludeTest :: IO TestCtx -> PosTest -> TestTree
mkPreludeTest getCtx p = testCase (unpack (p ^. posTestName)) (replTest (p ^. posTestInput) (p ^. posTestExpected) getCtx)

replSetup :: IO TestCtx
replSetup = do
Expand All @@ -52,22 +65,57 @@ replSetup = do
replTeardown :: TestCtx -> IO ()
replTeardown = removeDirRecur . (^. testCtxRootDir)

replTest :: IO TestCtx -> IO ()
replTest getTestCtx = do
replTest :: Text -> Core.Value -> IO TestCtx -> IO ()
replTest input' expectedNode getTestCtx = do
ctx <- getTestCtx
(_, res) <- compileReplInputIO' ctx "1 + 1"
case res of
Left err -> assertFailure "err"
Right Nothing -> assertFailure "nothing"
Right n -> assertBool "expected equal" (n == (Just (Core.mkConstant' (Core.ConstInteger 2))))
(artif, res) <- compileReplInputIO' ctx input'
res' <- assertNoJuvixError res
case res' of
Nothing -> assertFailure "Compilation did not return a node"
Just n -> do
let ep = ctx ^. testCtxEntryPoint
n' <- evalRepl artif ep n
assertValueEqual expectedNode n'

mkInteger :: Integer -> Core.Value
mkInteger = Core.ValueConstant . Core.ConstInteger

mkBool :: Bool -> Core.Value
mkBool b =
Core.ValueConstrApp
( Core.ConstrApp
{ _constrAppName = name,
_constrAppFixity = Nothing,
_constrAppArgs = []
}
)
where
name :: Text
name = case b of
True -> Str.true
False -> Str.false

allTests :: TestTree
allTests = withResource
replSetup
replTeardown
(\getTestCtx -> testGroup "REPL positive tests"
(map (\f -> f getTestCtx ) [testCase "repl test" . replTest]))
allTests =
testGroup
"REPL positive tests"
[ withResource
replSetup
replTeardown
( \getCtx ->
testGroup
"Loading Stdlib.Prelude"
( map
(mkPreludeTest getCtx)
[ PosTest "Arithmetic" "3 * (1 + 1)" (mkInteger 6),
PosTest "Logic And" "true && false" (mkBool False),
PosTest "Let" "let x : Nat := 2 + 1 in x" (mkInteger 3),
PosTest "Literal comparison" "1 == 1" (mkBool True),
PosTest "List literal in call" "head 0 [1;2;3]" (mkInteger 1)
]
)
)
]

compileReplInputIO' :: TestCtx -> Text -> IO (Artifacts, (Either JuvixError (Maybe Core.Node)))
compileReplInputIO' ctx txt =
Expand All @@ -83,3 +131,27 @@ compileReplInputIO' ctx txt =
ReplPipelineResultNode n -> Just n
ReplPipelineResultImport {} -> Nothing
ReplPipelineResultOpen {} -> Nothing

evalRepl :: Artifacts -> EntryPoint -> Core.Node -> IO Core.Value
evalRepl artif ep n = do
(artif', n') <-
assertNoJuvixError
. run
. runReader ep
. runError @JuvixError
. runState artif
. runTransformations True toStoredTransformations
$ n
doEvalIO' artif' n' >>= assertNoJuvixError
where
doEvalIO' :: Artifacts -> Core.Node -> IO (Either JuvixError Core.Value)
doEvalIO' artif' n' =
mapRight (Core.toValue tab)
. mapLeft (JuvixError @Core.CoreError)
<$> (Core.doEvalIO False replDefaultLoc tab n')
where
tab :: Core.InfoTable
tab = Core.computeCombinedInfoTable $ artif' ^. artifactCoreModule

replDefaultLoc :: Interval
replDefaultLoc = singletonInterval (mkInitialLoc P.replPath)
30 changes: 0 additions & 30 deletions tests/smoke/Commands/repl.smoke.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -227,16 +227,6 @@ tests:
Nat
exit-status: 0

- name: eval-let-expression
command:
- juvix
- repl
stdin: "let x : Nat := 2 + 1 in x"
stdout:
contains:
"3"
exit-status: 0

- name: load-builtin-bool
command:
shell:
Expand Down Expand Up @@ -299,16 +289,6 @@ tests:
Stdlib.Prelude> .*/global-project/
exit-status: 0

- name: eval-adding-two-literal-nats
command:
- juvix
- repl
stdin: "1 + 2"
stdout:
contains: |
3
exit-status: 0

- name: repl-trace
command:
- juvix
Expand Down Expand Up @@ -453,16 +433,6 @@ tests:
contains: "true"
exit-status: 0

- name: literal-comparison
command:
- juvix
- repl
stdin: |
1 == 1
stdout:
contains: "true"
exit-status: 0

- name: open-import-from-stdlib
command:
- juvix
Expand Down

0 comments on commit 51187dd

Please sign in to comment.