module Test.Angle.Exec.Exec ( tests ) where import TestHelper appending :: [a] -> [[a]] -> [a] appending x = concatMap (++x) setupAddF :: String setupAddF = appending "\n" [defIsInt, defAddInts] defIsInt :: String defIsInt = defun "isInt" "x" "return (== x asType(0, x));" defAddInts :: String defAddInts = defun "addInts" "x:@isInt, y:@isInt" "(+ x y);" defun :: String -> String -> String -> String defun n a b = concat ["defun ", n, "(", a, ") ", b] checkRes :: String -> (LangLit -> Bool) -> Property checkRes s r = monadicIO $ runEx s >>= (assert . r) checkFail :: String -> Property checkFail s = expectFailure $ monadicIO $ runEx s checkResEq :: String -> LangLit -> Property checkResEq s x = checkRes s (==x) testLess :: Int -> Int -> Property testLess = binTest LitBool (<) "<" testGreater :: Int -> Int -> Property testGreater = binTest LitBool (>) ">" testLessEq :: Int -> Int -> Property testLessEq = binTest LitBool (<=) "<=" testGreaterEq :: Int -> Int -> Property testGreaterEq = binTest LitBool (>=) ">=" testAdd :: NonEmptyList Int -> Property testAdd (NonEmpty xs) = opTest LitInt sum "+" xs testAnd :: NonEmptyList Bool -> Property testAnd (NonEmpty xs) = opTest LitBool and "&" xs testSub :: NonEmptyList Int -> Property testSub (NonEmpty xs) = opTest LitInt (foldl1 (-)) "-" xs testMult :: NonEmptyList Int -> Property testMult (NonEmpty xs) = opTest LitInt product "*" xs testNot :: Bool -> Property testNot = opTestUnary LitBool not "^" testOr :: NonEmptyList Bool -> Property testOr (NonEmpty xs) = opTest LitBool or "|" xs -- | Helper function for testing binary operators. binTest :: (Show a, Show b) => (b -> LangLit) -> (a -> a -> b) -> String -> a -> a -> Property binTest t f op x y = checkResEq st (t $ f x y) where st = opElts op [show x, show y] opElts :: String -> [String] -> String opElts op xs = concat [ "(" , op, " " , appending " " (init xs) , last xs , ");"] opStr :: (a -> LangLit) -> String -> [a] -> String opStr t op xs = concat [ "(" , op, " " , appending " " (init xs') , last xs' , ");"] where xs' = map (showSyn . t) xs opTest :: (Show a) => (a -> LangLit) -> ([a] -> a) -> String -> [a] -> Property opTest t f op xs = checkResEq st (t $ f xs) where st = opStr t op xs opTestUnary :: (a -> LangLit) -> (a -> a) -> String -> a -> Property opTestUnary t f op x = checkResEq st (t $ f x) where st = concat [op, showSyn $ t x, ";"] callShow :: (Show a) => String -> [a] -> String callShow f xs = concat [ f, "(" , concatMap ((++", ") . show) (init xs) , show (last xs) , ");" ] callShowSyn :: (ShowSyn a) => String -> [a] -> String callShowSyn f xs = concat [ f, "(" , concatMap ((++", ") . showSyn) (init xs) , showSyn (last xs) , ");" ] testBuiltinLength :: TinyList LangLit -> Property testBuiltinLength (TinyList xs) = checkResEq toRun $ LitInt (length xs) where toRun = callShowSyn "length" [LitList xs] testBuiltinLengthRange :: NonNegative Int -> NonNegative Int -> NonNegative Int -> Property testBuiltinLengthRange (NonNegative x) (NonNegative y) (NonNegative z) = y > x && z > x ==> checkResEq toRun $ LitInt (length [x,z..y]) where toRun = callShowSyn "length" [LitRange (LitInt x) (Just $ LitInt y) (Just $ LitInt z)] testBuiltinIsNull :: LangLit -> Property testBuiltinIsNull x = checkResEq toRun $ LitBool (x == LitNull) where toRun = callShowSyn "isNull" [x] testBuiltinIndexBasic :: TinyList LangLit -> NonNegative Int -> Property testBuiltinIndexBasic (TinyList xs) (NonNegative x) = x < length xs ==> checkResEq toRun $ xs !! x where toRun = callShowSyn "index" [LitInt x, LitList xs] testBuiltinStr :: LangLit -> Property testBuiltinStr x = checkResEq toRun $ case x of LitStr _ -> x LitChar c -> LitStr [c] r@(LitRange{}) -> LitStr $ showSyn r x' -> LitStr (showSyn x') where toRun = callShowSyn "str" [x] testClassAdd :: LangLit -> LangLit -> Property testClassAdd (LitInt x) (LitInt y) = checkResEq toRun $ LitInt (x + y) where toRun = setupAddF ++ callShow "addInts" [x, y] testClassAdd x y = checkFail toRun where toRun = setupAddF ++ callShowSyn "addInts" [x, y] testReturnSimple :: NonLambda -> Property testReturnSimple (NonLambda x) = checkResEq toRun x where toRun = setupReturnSimple ++ callShowSyn "returnSimple" [x] setupReturnSimple = defun "returnSimple" "x" "return x;" newtype NonLambda = NonLambda LangLit deriving (Show) instance Arbitrary NonLambda where arbitrary = liftM NonLambda $ arbitrary `suchThat` (\x -> case x of LitLambda{} -> False _ -> True) testReturnIfEmbedded :: Bool -> NonLambda -> NonLambda -> Property testReturnIfEmbedded p (NonLambda x) (NonLambda y) | p = checkResEq toRun x | otherwise = checkResEq toRun y where toRun = setupReturnIfEmbedded ++ callShowSyn "returnIfEmbedded" [LitBool p, x, y] setupReturnIfEmbedded = defun "returnIfEmbedded" "p, x, y" "if p then return x; else return y;" testForLoopSimple :: TinyList LangLit -> Property testForLoopSimple (TinyList xs) = checkResEq toRun (LitList xs) where toRun = for "i" (showSyn $ LitList xs) "i;" testForLoopBreakSimple :: NonEmptyList LangLit -> Property testForLoopBreakSimple (NonEmpty xs) = checkResEq toRun (head xs) where toRun = for "i" (showSyn $ LitList xs) (multiStmt ["i;", "break;"]) testForLoopBreakWithValue :: NonEmptyList LangLit -> LangLit -> Property testForLoopBreakWithValue (NonEmpty xs) y = checkResEq toRun y where toRun = for "i" (showSyn $ LitList xs) (multiStmt ["i;", "break " ++ showSyn y ++ ";"]) multiStmt :: [String] -> String multiStmt xs = concat ["{", concat xs, "}"] for :: String -> String -> String -> String for ident expr body = concat [ "for ", ident, " in " , expr, " do ", body] tests :: [TestTree] tests = [ testGroup "filter tests" [ ] , testGroup "basic operators" [ testProperty "less" testLess , testProperty "add" testAdd , testProperty "mult" testMult , testProperty "not" testNot , testProperty "sub" testSub , testProperty "and" testAnd , testProperty "or" testOr , testProperty "lessEq" testLessEq , testProperty "greaterEq" testGreaterEq , testProperty "greater" testGreater ] , testGroup "classes" [ testProperty "isAdd" testClassAdd ] , testGroup "basic functions" [ testProperty "returnSimple" testReturnSimple , testProperty "returnIfEmbedded" testReturnIfEmbedded ] , testGroup "structures" [ testProperty "simple for-loop" testForLoopSimple , testProperty "for loop break - simple" testForLoopBreakSimple , testProperty "for loop break - value" testForLoopBreakWithValue ] , testGroup "builtin functions" [ testProperty "length" testBuiltinLength , testProperty "length with range" testBuiltinLengthRange , testProperty "isNull" testBuiltinIsNull , testProperty "index - basic" testBuiltinIndexBasic , testProperty "str" testBuiltinStr ] ]