{-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE FlexibleInstances #-} module TestHelper ( module Test.Tasty , module Test.Tasty.QuickCheck , module Angle.Types.Lang , assert , Parser , evalParse , monadicEither , liftM , monadicIO , assertEqual , run , maxSized , runExecIOBasic , runExec , runEx , SmallList(..) , TinyList(..) ) where import Control.Applicative ((<*>), (<$>)) import Control.Monad.State import Data.Char (isAlpha, isAlphaNum) import qualified Data.Map as M import Test.QuickCheck import Test.QuickCheck.Monadic import Test.Tasty import Test.Tasty.QuickCheck import Angle.Parse.Helpers (evalParse, Parser) import Angle.Parse.Token (keywords) import Angle.Exec.Types.Internal import Angle.Scanner (SourcePos(..)) import Angle.Types.Lang import Angle.Types.Scope import Angle.Parse.Parser (program) import Angle.Exec.Exec (execStmt) instance Arbitrary LangLit where arbitrary = frequency [ (7, liftArby (LitStr . getValidLitStr)) , (9, liftArby LitInt) , (9, liftArby LitFloat) , (1, liftM LitList (liftArby getTinyList)) , (9, liftArby LitBool) , (1, arbyRange) , (6, liftArby LitChar) , (1, liftArby LitLambda) --, (9, return LitNull) ] where arbyRange = do x <- suchThat arbitrary enumType [y,z] <- suchThat (vector 2) (allType (typeOf x)) (b1, b2) <- arbitrary let y' = if b1 then Just y else Nothing z' = if b2 then Just z else Nothing return $ LitRange x y' z' instance Arbitrary SingStmt where arbitrary = frequency [ (7, liftArby2 StmtAssign) , (2, liftArby StmtStruct) , (5, liftArby StmtExpr) , (8, liftArby StmtReturn) ] shrink (StmtAssign x y) = shrink2 StmtAssign x y shrink (StmtStruct x) = shrink1 StmtStruct x shrink (StmtExpr x) = shrink1 StmtExpr x shrink (StmtReturn x) = shrink1 StmtReturn x shrink _ = undefined instance Arbitrary LangStruct where arbitrary = frequency [ (3, liftArby3 StructFor) , (3, liftArby2 StructWhile) , (3, liftM3 StructIf arbitrary (liftArby MultiStmt) arbitrary) , (1, liftArby2 StructDefun) ] shrink (StructFor x y z) = shrink3 StructFor x y z shrink (StructWhile x y) = shrink2 StructWhile x y shrink (StructIf x y z) = shrink3 StructIf x y z shrink (StructDefun x y) = shrink2 StructDefun x y shrink (StructTryCatch x y) = shrink2 StructTryCatch x y instance Arbitrary Lambda where arbitrary = do args <- arbitrary body <- arbitrary return Lambda { lambdaArgs=args, lambdaBody=body, lambdaScope = Nothing} shrink (Lambda x y z) = shrink3 Lambda x y z instance Arbitrary Expr where arbitrary = frequency [ (15, liftArby ExprIdent) , (9, liftArby ExprLit) , (1, liftM3 ExprFunCall arbitrary arbitrary (liftArby getTinyList)) , (4, liftArby ExprOp) , (4, liftArby ExprFunIdent) ] instance Arbitrary ArgSig where arbitrary = do args <- liftArby getTinyList catchArg <- arbitrary return ArgSig { Angle.Types.Lang.stdArgs = args, catchAllArg = catchArg } shrink (ArgSig x y) = shrink2 ArgSig x y instance Arbitrary ArgElt where arbitrary = liftArby3 ArgElt shrink (ArgElt x y z) = shrink3 ArgElt x y z instance Arbitrary CatchArg where arbitrary = liftArby2 CatchArg shrink1 :: Arbitrary a => (a -> b) -> a -> [b] shrink1 f x = map f (shrink x) shrink2 :: (Arbitrary a, Arbitrary b) => (a -> b -> c) -> a -> b -> [c] shrink2 f x y = zipWith f (shrink x) (shrink y) shrink3 :: (Arbitrary c, Arbitrary b, Arbitrary a) => (a -> b -> c -> d) -> a -> b -> c -> [d] shrink3 f x y z = zipWith3 f (shrink x) (shrink y) (shrink z) instance Arbitrary ConstrRef where arbitrary = liftArby2 ConstrRef shrink (ConstrRef x y) = shrink2 ConstrRef x y instance Arbitrary AnnType where arbitrary = elements [AnnFun, AnnLit] shrink AnnFun = [AnnLit] shrink AnnLit = [AnnFun] shrink _ = undefined instance Arbitrary Stmt where arbitrary = frequency [ (9, liftArby2 SingleStmt) , (1, liftM MultiStmt (liftArby getTinyList)) ] shrink (SingleStmt x p) = shrink2 SingleStmt x p shrink (MultiStmt xs) = shrink1 MultiStmt xs instance Arbitrary LangOp where arbitrary = frequency [ (7, liftM SpecOp (liftArby getSpecOp) >>= checkOp) , (3, liftM MultiOp (liftArby getMultiOp) >>= liftArby) ] where checkOp f = do -- Prevent -ve on numbers x <- arbitrary case f x of (SpecOp OpNeg (ExprLit (LitInt _))) -> arbitrary (SpecOp OpNeg (ExprLit (LitFloat _))) -> arbitrary r -> return r shrink (SpecOp x y) = shrink2 SpecOp x y shrink (MultiOp x ys) = shrink2 MultiOp x ys instance Arbitrary Op where arbitrary = oneof [ liftArby getMultiOp , liftArby getSpecOp] liftArby :: (Arbitrary a) => (a -> b) -> Gen b liftArby f = liftM f arbitrary liftArby2 :: (Arbitrary a, Arbitrary b) => (a -> b -> c) -> Gen c liftArby2 f = liftM2 f arbitrary arbitrary liftArby3 :: (Arbitrary a, Arbitrary b, Arbitrary c) => (a -> b -> c -> d) -> Gen d liftArby3 f = liftM3 f arbitrary arbitrary arbitrary newtype ValidLitStr = ValidLitStr { getValidLitStr :: String } instance Arbitrary ValidLitStr where arbitrary = liftArby (ValidLitStr . filter (/='"')) instance Arbitrary LangIdent where arbitrary = liftM LangIdent $ validIdent `suchThat` isValidIdent shrink (LangIdent x) = map LangIdent (filter isValidIdent (shrink x)) validIdent :: Gen String validIdent = (:) <$> chooseAlpha <*> listOf chooseAlphaNum where chooseAlpha = oneof [choose ('a','z'), choose ('A','Z')] chooseDigit = choose ('0','9') chooseAlphaNum = oneof [chooseAlpha, chooseDigit] newtype SpecOp = ArbySpecOp { getSpecOp :: Op } newtype MultiOp = ArbyMultiOp { getMultiOp :: Op } instance Arbitrary SpecOp where arbitrary = elements $ map ArbySpecOp [OpNeg, OpNot] instance Arbitrary MultiOp where arbitrary = elements $ map ArbyMultiOp [ OpAdd , OpAnd , OpConcat , OpDiv , OpEq , OpGreater , OpGreaterEq , OpLess , OpLessEq , OpMult , OpOr , OpSub ] newtype ValidComment = ValidComment String instance Arbitrary ValidComment where arbitrary = liftM ValidComment $ arbitrary `suchThat` isValidComment where isValidComment x | '\n' `elem` x = False | otherwise = True maxSmallListLength :: Int maxSmallListLength = 50 maxTinyListLength :: Int maxTinyListLength = 10 newtype SmallList a = SmallList { getSmallList :: [a] } deriving (Show) newtype TinyList a = TinyList { getTinyList :: [a] } deriving (Show) instance (Arbitrary a) => Arbitrary (SmallList a) where arbitrary = sized $ \s -> do n <- choose (0,s`min`maxSmallListLength) xs <- vector n return (SmallList xs) shrink (SmallList xs) = shrink1 SmallList xs instance (Arbitrary a) => Arbitrary (TinyList a) where arbitrary = sized $ \s -> do n <- choose (0,s`min`maxTinyListLength) xs <- vector n return (TinyList xs) shrink (TinyList xs) = shrink1 TinyList xs instance (Arbitrary a) => Arbitrary (BindEnv LangIdent a) where arbitrary = liftArby bindEnvFromList shrink (BindEnv x) = shrink1 bindEnvFromList (M.toList x) instance (Arbitrary a) => Arbitrary (VarVal a) where arbitrary = liftArby2 VarVal shrink (VarVal x y) = shrink2 VarVal x y instance Arbitrary Scope where arbitrary = liftArby3 Scope shrink (Scope w x y) = shrink3 Scope w x y -- | Extracts a property from monadic Either code, giving -- a failing property if the result is a Left. monadicEither :: PropertyM (Either e) a -> Property monadicEither = monadic (\e -> case e of Left _ -> property False Right r -> r) runExec :: ExecIO a -> IO a runExec e = do x <- runExecIOBasic e case x of Left _ -> fail "runExec failed" Right r -> return r runEx :: String -> PropertyM IO LangLit runEx s = let (Right r) = evalParse s program in run $ runExec $ execStmt r instance Arbitrary LangType where arbitrary = elements langTypes shrink x = filter (/=x) langTypes langTypes :: [LangType] langTypes = [LTStr, LTInt, LTFloat, LTList , LTBool, LTRange, LTNull ] instance Arbitrary SourceRef where arbitrary = do start <- arbitrary end <- arbitrary return $ SourceRef (start, end) shrink (SourceRef x) = shrink1 SourceRef x instance Arbitrary SourcePos where arbitrary = do f <- liftArby getPositive s <- liftArby getPositive t <- liftArby getPositive return $ SourcePos (f, s, t) shrink (SourcePos x) = shrink1 SourcePos x assertEqual :: (Monad m, Eq a) => a -> a -> PropertyM m () assertEqual x = assert . (==x) maxSized :: (Testable prop) => Int -> prop -> Property maxSized x = mapSize (min x) isValidIdent :: String -> Bool isValidIdent [] = False isValidIdent xs | xs `elem` keywords = False isValidIdent (x:xs) = isAlpha x && all isAlphaNum xs