packages feed

stgi-1: test/Testsuite/Test/Machine/Evaluate/Errors.hs

{-# LANGUAGE NumDecimals       #-}
{-# LANGUAGE OverloadedLists   #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes       #-}

module Test.Machine.Evaluate.Errors (tests) where



import Test.Tasty

import Stg.Language
import Stg.Machine.Types
import Stg.Parser.QuasiQuoter

import Test.Machine.Evaluate.TestTemplates.MachineState
import Test.Orphans                                     ()



tests :: TestTree
tests = testGroup "Error conditions"
    [ updatableClosureWithArgs
    , returnIntWithEmptyReturnStack
    , updateClosureWithPrimitive
    , functionArgumentNotInScope
    , constructorArgumentNotInScope
    , primitiveArgumentNotInScope
    , algebraicReturnToPrimitiveAlts
    , primReturnToAlgAlts
    , loopEnterBlackHole
    , functionScrutinee
    , testGroup "Invalid operations"
        [ divisionByZero
        , moduloZero ]
    , badConArity
    ]

updatableClosureWithArgs :: TestTree
updatableClosureWithArgs = machineStateTest defSpec
    { testName = "Updatable closure with arguments"
    , source = Program (Binds
        [(Var "main",
            LambdaForm [] Update [Var "x"] (AppF (Var "x") []) )])
    , successPredicate = \state -> case stgInfo state of
        Info (StateError UpdatableClosureWithArgs) _ -> True
        _otherwise -> False }

returnIntWithEmptyReturnStack :: TestTree
returnIntWithEmptyReturnStack = machineStateTest defSpec
    { testName = "Closure with primitive body"
    , source = [stg| main = \ -> case 1# of v -> v |]
    , successPredicate = \state -> case stgInfo state of
        Info (StateError ReturnIntWithEmptyReturnStack) _ -> True
        _otherwise -> False }

updateClosureWithPrimitive :: TestTree
updateClosureWithPrimitive = machineStateTest defSpec
    { testName = "Closure update with primitive"
    , source = [stg| main = \ => case 1# of v -> v |]
    , successPredicate = \state -> case stgInfo state of
        Info (StateError UpdateClosureWithPrimitive) _ -> True
        _otherwise -> False }

functionArgumentNotInScope :: TestTree
functionArgumentNotInScope = machineStateTest defSpec
    { testName = "Function argument not in scope"
    , source = [stg| main = \ -> main x |]
    , successPredicate = \state -> case stgInfo state of
        Info (StateError VariablesNotInScope{}) _ -> True
        _otherwise -> False }

constructorArgumentNotInScope :: TestTree
constructorArgumentNotInScope = machineStateTest defSpec
    { testName = "Function argument not in scope"
    , source = [stg| main = \ -> Con x |]
    , successPredicate = \state -> case stgInfo state of
        Info (StateError VariablesNotInScope{}) _ -> True
        _otherwise -> False }

primitiveArgumentNotInScope :: TestTree
primitiveArgumentNotInScope = machineStateTest defSpec
    { testName = "Primitive function argument not in scope"
    , source = [stg| main = \ -> case +# x y of default -> Foo |]
    , successPredicate = \state -> case stgInfo state of
        Info (StateError VariablesNotInScope{}) _ -> True
        _otherwise -> False }

algebraicReturnToPrimitiveAlts :: TestTree
algebraicReturnToPrimitiveAlts = machineStateTest defSpec
    { testName = "Algebraic scrutinee with primitive alts"
    , source = [stg| main = \ -> case Con of 1# -> A; default -> B |]
    , successPredicate = \state -> case stgInfo state of
        Info (StateError AlgReturnToPrimAlts) _ -> True
        _otherwise -> False }

primReturnToAlgAlts :: TestTree
primReturnToAlgAlts = machineStateTest defSpec
    { testName = "Primitive scrutinee with algebraic alts"
    , source = [stg| main = \ -> case 1# of Con -> A; default -> B |]
    , successPredicate = \state -> case stgInfo state of
        Info (StateError PrimReturnToAlgAlts) _ -> True
        _otherwise -> False }

loopEnterBlackHole :: TestTree
loopEnterBlackHole = machineStateTest defSpec
    { testName = "Loop on entering black hole"
    , source = [stg| main = \ => main |]
    , successPredicate = \state -> case stgInfo state of
        Info (StateError EnterBlackhole) _ -> True
        _otherwise -> False }

functionScrutinee :: TestTree
functionScrutinee = machineStateTest defSpec
    { testName = "Function scrutinee"
    , failWithInfo = False
    , source = [stg|
        id = \x -> x;
        main = \ => case id of
            default -> Success
        |]
    , successPredicate = \state -> case stgInfo state of
        Info (StateError NonAlgPrimScrutinee) _ -> True
        _otherwise -> False }

divisionByZero :: TestTree
divisionByZero = machineStateTest defSpec
    { testName = "Division by zero"
    , failWithInfo = False
    , source = [stg|
        main = \ => case /# 1# 0# of
            default -> Failure
        |]
    , successPredicate = \state -> case stgInfo state of
        Info (StateError DivisionByZero) _ -> True
        _otherwise -> False }

moduloZero :: TestTree
moduloZero = machineStateTest defSpec
    { testName = "Modulo by zero"
    , failWithInfo = False
    , source = [stg|
        main = \ => case %# 1# 0# of
            default -> Failure
        |]
    , successPredicate = \state -> case stgInfo state of
        Info (StateError DivisionByZero) _ -> True
        _otherwise -> False }

badConArity :: TestTree
badConArity = machineStateTest defSpec
    { testName = "Bad constructor arity"
    , failWithInfo = False
    , source = [stg|
        x = \ -> Unit;
        y = \ -> Unit;
        main = \ => case Cons x y of
            Cons x -> Bad;
            default -> Baaad
        |]
    , successPredicate = \state -> case stgInfo state of
        Info (StateError BadConArity{}) _ -> True
        _otherwise -> False }