import Test.Tasty
import Test.Tasty.HUnit
import Test.Tasty.QuickCheck as QC
import Test.Tasty.Golden as TG
import System.Process
import Text.ParserCombinators.Parsec
import NamedLambda
import Lambda
import Stlc.Types
import Ski
import Environment
import Interpreter
main :: IO ()
main = defaultMain tests
tests :: TestTree
tests = testGroup "Tests"
[ parserTests
, typeinferTests
, skiabsTests
, lambdaProps
, parserProps
, goldenTests
]
-- Unit tests
parserTests :: TestTree
parserTests = testGroup "Parser tests"
[ testCase "INR parser test" $
parse lambdaexp "" "INR (\\x.y)"
@?=
Right (TypedInr (LambdaAbstraction "x" (LambdaVariable "y")))
, testCase "INL parser test" $
parse lambdaexp "" "INL (\\ab.cd)"
@?=
Right (TypedInl (LambdaAbstraction "ab" (LambdaVariable "cd")))
, testCase "Spaces between lambdas test" $
parse lambdaexp "" "(\\x. x)"
@?=
Right (LambdaAbstraction "x" (LambdaVariable "x"))
, testCase "Multiple-character variable name test" $
parse lambdaexp "" "(\\asdf. asdf)"
@?=
Right (LambdaAbstraction "asdf" (LambdaVariable "asdf"))
]
typeinferTests :: TestTree
typeinferTests = testGroup "Type inference tests"
[ testCase "Identity type inference" $
typeinference (Lambda (Var 1))
@?=
Just (Arrow (Tvar 0) (Tvar 0))
, testCase "Double negation of LEM" $
typeinference (Lambda (Absurd (App (Var 1) (Inr (Lambda (App (Var 2) (Inl (Var 1))))))))
@?=
Just (Arrow (Arrow (Union (Tvar 0) (Arrow (Tvar 0) Bottom)) Bottom) Bottom)
]
skiabsTests :: TestTree
skiabsTests = testGroup "SKI abstraction tests"
[ testCase "Identity SKI abstraction" $
skiabs (LambdaAbstraction "x" (LambdaVariable "x"))
@?=
I
, testCase "Numeral 2 SKI abstraction" $
skiabs (LambdaAbstraction "a"
(LambdaAbstraction "b"
(LambdaApplication (LambdaVariable "a")
(LambdaApplication (LambdaVariable "a") (LambdaVariable "b")))))
@?=
Comb (Comb S (Comb (Comb S (Comb K S)) K)) I
]
-- Quickcheck
lambdaProps :: TestTree
lambdaProps = testGroup "Lambda expression properties (quickcheck)"
[ QC.testProperty "Expression -> named -> expression" $
\expr -> toBruijn emptyContext (nameExp expr) == expr
, QC.testProperty "Open expressions are not allowed" $
\expr -> not (isOpenExp expr)
]
parserProps :: TestTree
parserProps = testGroup "Parser properties (quickcheck)"
[ QC.testProperty "Comments are ignored" $
\str -> case parse interpreteractionParser "" ("#" ++ str) of
Right (Interpret Comment) -> True
_ -> False
]
-- Arbitrary typed lambda expressions
instance Arbitrary Exp where
arbitrary = sized (lambda 0)
lambda :: Int -> Int -> Gen Exp
lambda 0 0 = return $ Lambda (Var 1)
lambda 0 size = Lambda <$> lambda 1 (size-1)
lambda lim 0 = Var <$> (toInteger <$> choose (1, lim))
lambda lim size = oneof
[ Var <$> (toInteger <$> choose (1, lim))
, Lambda <$> lambda (succ lim) (size-1)
, App <$> lambda lim (div size 2) <*> lambda lim (div size 2)
, Pair <$> lambda lim (div size 2) <*> lambda lim (div size 2)
, Pi1 <$> (Pair <$> lambda lim (div size 2) <*> lambda lim (div size 2))
, Pi2 <$> (Pair <$> lambda lim (div size 2) <*> lambda lim (div size 2))
, Inl <$> lambda lim (size-1)
, Inr <$> lambda lim (size-1)
, Caseof <$> (Inl <$> lambda lim (div size 3))
<*> (Lambda <$> lambda (succ lim) (div size 3))
<*> (Lambda <$> lambda (succ lim) (div size 3))
, Caseof <$> (Inr <$> lambda lim (div size 3))
<*> (Lambda <$> lambda (succ lim) (div size 3))
<*> (Lambda <$> lambda (succ lim) (div size 3))
, return Unit
, Abort <$> lambda lim (size-1)
, Absurd <$> lambda lim (size-1)
]
-- Golden test
goldenTests :: TestTree
goldenTests = testGroup "Golden tests (tasty-golden)"
[ goldenVsFile "Main golden test"
"tests/goldenfile.golden"
"tests/testingfile"
(callCommand "mikrokosmos tests/testing.mkr > tests/testingfile")
]