Moved all the testPass* functions into the TestMonad, using unsafePerformIO for running them inside QuickCheck

This commit is contained in:
Neil Brown 2008-03-05 16:06:14 +00:00
parent d02b771572
commit 256ce80ccb
3 changed files with 40 additions and 31 deletions

View File

@ -22,6 +22,7 @@ module TestFramework where
import Control.Monad.Error import Control.Monad.Error
import Data.Generics import Data.Generics
import System.IO.Unsafe
import Test.HUnit hiding (Testable) import Test.HUnit hiding (Testable)
import Test.QuickCheck hiding (check) import Test.QuickCheck hiding (check)
@ -34,14 +35,17 @@ instance Error Result where
class Monad m => TestMonad m r | m -> r where class Monad m => TestMonad m r | m -> r where
runTest :: m () -> r runTest :: m () -> r
testFailure :: String -> m () testFailure :: String -> m ()
runIO :: IO a -> m a
instance TestMonad IO Assertion where instance TestMonad IO Assertion where
runTest = id runTest = id
testFailure = assertFailure testFailure = assertFailure
runIO = id
instance TestMonad (Either Result) Result where instance TestMonad (Either Result) Result where
runTest = either id (const $ Result (Just True) [] []) runTest = either id (const $ Result (Just True) [] [])
testFailure s = Left $ Result (Just False) [] [s] testFailure s = Left $ Result (Just False) [] [s]
runIO f = return (unsafePerformIO f)
compareForResult :: TestMonad m r => String -> (a -> String) -> (a -> a -> Bool) -> a -> a -> m () compareForResult :: TestMonad m r => String -> (a -> String) -> (a -> a -> Bool) -> a -> a -> m ()
compareForResult msg showFunc cmpFunc exp act compareForResult msg showFunc cmpFunc exp act

View File

@ -54,6 +54,7 @@ import Metadata (emptyMeta)
import Pass import Pass
import Pattern import Pattern
import PrettyShow import PrettyShow
import TestFramework
import TreeUtils import TreeUtils
import Types import Types
import Utils import Utils
@ -281,18 +282,18 @@ assertItemNotSame msg items key0 key1 = assertNotEqual msg ((Map.lookup key0 ite
-- It takes an expected value, a transformed value (wrapped in the 'PassM' monad), an initial state-changing function, and returns the subsequent -- It takes an expected value, a transformed value (wrapped in the 'PassM' monad), an initial state-changing function, and returns the subsequent
-- state, with either an assertion (if the pass failed) or the 'Items' (if the pass succeeded) -- state, with either an assertion (if the pass failed) or the 'Items' (if the pass succeeded)
testPassGetItems :: testPassGetItems ::
(Data a, Data b) => (Data a, Data b, TestMonad m r) =>
String -- ^ The message\/test name to prefix on failure. String -- ^ The message\/test name to prefix on failure.
-> a -- ^ The expected outcome of the pass. Will be used as a 'Pattern', to find the named items in the result of the pass. -> a -- ^ The expected outcome of the pass. Will be used as a 'Pattern', to find the named items in the result of the pass.
-> PassM b -- ^ The actual pass. -> PassM b -- ^ The actual pass.
-> (State CompState ()) -- ^ A function to transform a 'CompState'. Will be used on the 'emptyState' to get the initial state for the pass. -> (State CompState ()) -- ^ A function to transform a 'CompState'. Will be used on the 'emptyState' to get the initial state for the pass.
-> IO (CompState, Either Assertion Items) -- ^ Returns the state, along with either an 'Assertion' (if the pass fails) or the 'Items' (if the pass succeeds). -> m (CompState, Either (m ()) Items) -- ^ Returns the state, along with either an 'Assertion' (if the pass fails) or the 'Items' (if the pass succeeds).
testPassGetItems testName expected actualPass startStateTrans = testPassGetItems testName expected actualPass startStateTrans =
--passResult :: Either String b --passResult :: Either String b
do passResult <- runPass actualPass startState do passResult <- runPass actualPass startState
case passResult of case passResult of
(st, Left (_, err)) -> return (st, Left $ assertFailure (prefixErr $ "pass actually failed: " ++ err)) (st, Left (_, err)) -> return (st, Left $ testFailure (prefixErr $ "pass actually failed: " ++ err))
(st, Right resultItem) -> return (st, transformEither (mapM_ (assertFailure . prefixErr)) (id) $ getMatchedItems expected resultItem) (st, Right resultItem) -> return (st, transformEither (mapM_ (testFailure . prefixErr)) (id) $ getMatchedItems expected resultItem)
where where
startState :: CompState startState :: CompState
startState = execState startStateTrans emptyState startState = execState startStateTrans emptyState
@ -301,47 +302,47 @@ testPassGetItems testName expected actualPass startStateTrans =
prefixErr err = testName ++ ": " ++ err prefixErr err = testName ++ ": " ++ err
-- | Runs a given AST pass and returns the subsequent state, along with either an error or the result. This function is primarily intended for internal use by this module. -- | Runs a given AST pass and returns the subsequent state, along with either an error or the result. This function is primarily intended for internal use by this module.
runPass :: runPass :: TestMonad m r =>
PassM b -- ^ The actual pass. PassM b -- ^ The actual pass.
-> CompState -- ^ The state to use to run the pass. -> CompState -- ^ The state to use to run the pass.
-> IO (CompState, Either ErrorReport b) -- ^ The resultant state, and either an error or the successful outcome of the pass. -> m (CompState, Either ErrorReport b) -- ^ The resultant state, and either an error or the successful outcome of the pass.
runPass actualPass startState = (liftM (\((x,y),_) -> (y,x))) (runWriterT $ runStateT (runErrorT actualPass) startState) runPass actualPass startState = liftM (\((x,y),_) -> (y,x)) $ runIO (runWriterT $ runStateT (runErrorT actualPass) startState)
-- | A test that runs a given AST pass and checks that it succeeds. -- | A test that runs a given AST pass and checks that it succeeds.
testPass :: testPass ::
(Data a, Data b) => (Data a, Data b, TestMonad m r) =>
String -- ^ The test name. String -- ^ The test name.
-> a -- ^ The expected value. Can either be an actual AST, or a 'Pattern' to match an AST. -> a -- ^ The expected value. Can either be an actual AST, or a 'Pattern' to match an AST.
-> PassM b -- ^ The actual pass. -> PassM b -- ^ The actual pass.
-> (State CompState ()) -- ^ A function to transform a 'CompState'. Will be used on the 'emptyState' to get the initial state for the pass. -> (State CompState ()) -- ^ A function to transform a 'CompState'. Will be used on the 'emptyState' to get the initial state for the pass.
-> Assertion -> m ()
--If Items are returned by testPassGetItems we return () [i.e. give an empty assertion], otherwise give back the assertion: --If Items are returned by testPassGetItems we return () [i.e. give an empty assertion], otherwise give back the assertion:
testPass w x y z = join $ liftM (either (id) (\x -> return ())) $ (liftM snd) $ (testPassGetItems w x y z) testPass w x y z = join $ liftM (either (id) (\x -> return ())) $ (liftM snd) $ (testPassGetItems w x y z)
-- | A test that runs a given AST pass and checks that it succeeds, and performs an additional check on the result -- | A test that runs a given AST pass and checks that it succeeds, and performs an additional check on the result
testPassWithCheck :: testPassWithCheck ::
(Data a, Data b) => (Data a, Data b, TestMonad m r) =>
String -- ^ The test name. String -- ^ The test name.
-> a -- ^ The expected value. Can either be an actual AST, or a 'Pattern' to match an AST. -> a -- ^ The expected value. Can either be an actual AST, or a 'Pattern' to match an AST.
-> PassM b -- ^ The actual pass. -> PassM b -- ^ The actual pass.
-> (State CompState ()) -- ^ A function to transform a 'CompState'. Will be used on the 'emptyState' to get the initial state for the pass. -> (State CompState ()) -- ^ A function to transform a 'CompState'. Will be used on the 'emptyState' to get the initial state for the pass.
-> (b -> Assertion) -> (b -> m ())
-> Assertion -> m ()
testPassWithCheck testName expected actualPass startStateTrans checkFunc = testPassWithCheck testName expected actualPass startStateTrans checkFunc =
do passResult <- runPass actualPass (execState startStateTrans emptyState) do passResult <- runPass actualPass (execState startStateTrans emptyState)
case snd passResult of case snd passResult of
Left (_,err) -> assertFailure (testName ++ "; pass actually failed: " ++ err) Left (_,err) -> testFailure (testName ++ "; pass actually failed: " ++ err)
Right result -> (assertPatternMatch testName expected result) >> (checkFunc result) Right result -> (testPatternMatch testName expected result) >> (checkFunc result)
-- | A test that runs a given AST pass, checks that it succeeds, and checks the resulting 'Items' with a given function. -- | A test that runs a given AST pass, checks that it succeeds, and checks the resulting 'Items' with a given function.
testPassWithItemsCheck :: testPassWithItemsCheck ::
(Data a, Data b) => (Data a, Data b, TestMonad m r) =>
String -- ^ The test name. String -- ^ The test name.
-> a -- ^ The expected value. Can either be an actual AST, or a 'Pattern' to match an AST. -> a -- ^ The expected value. Can either be an actual AST, or a 'Pattern' to match an AST.
-> PassM b -- ^ The actual pass. -> PassM b -- ^ The actual pass.
-> (State CompState ()) -- ^ A function to transform a 'CompState'. Will be used on the 'emptyState' to get the initial state for the pass. -> (State CompState ()) -- ^ A function to transform a 'CompState'. Will be used on the 'emptyState' to get the initial state for the pass.
-> (Items -> Assertion) -- ^ A function to check the 'Items' once the pass succeeds. -> (Items -> m ()) -- ^ A function to check the 'Items' once the pass succeeds.
-> Assertion -> m ()
testPassWithItemsCheck testName expected actualPass startStateTrans checkFunc = testPassWithItemsCheck testName expected actualPass startStateTrans checkFunc =
((liftM snd) (testPassGetItems testName expected actualPass startStateTrans)) ((liftM snd) (testPassGetItems testName expected actualPass startStateTrans))
>>= (\res -> >>= (\res ->
@ -352,13 +353,13 @@ testPassWithItemsCheck testName expected actualPass startStateTrans checkFunc =
-- | A test that runs a given AST pass, checks that it succeeds, and checks the resulting 'CompState' with a given function. -- | A test that runs a given AST pass, checks that it succeeds, and checks the resulting 'CompState' with a given function.
testPassWithStateCheck :: testPassWithStateCheck ::
(Data a, Data b) => (Data a, Data b, TestMonad m r) =>
String -- ^ The test name. String -- ^ The test name.
-> a -- ^ The expected value. Can either be an actual AST, or a 'Pattern' to match an AST. -> a -- ^ The expected value. Can either be an actual AST, or a 'Pattern' to match an AST.
-> PassM b -- ^ The actual pass. -> PassM b -- ^ The actual pass.
-> (State CompState ()) -- ^ A function to transform a 'CompState'. Will be used on the 'emptyState' to get the initial state for the pass. -> (State CompState ()) -- ^ A function to transform a 'CompState'. Will be used on the 'emptyState' to get the initial state for the pass.
-> (CompState -> Assertion) -- ^ A function to check the 'CompState' once the pass succeeds. -> (CompState -> m ()) -- ^ A function to check the 'CompState' once the pass succeeds.
-> Assertion -> m ()
testPassWithStateCheck testName expected actualPass startStateTrans checkFunc = testPassWithStateCheck testName expected actualPass startStateTrans checkFunc =
(testPassGetItems testName expected actualPass startStateTrans) (testPassGetItems testName expected actualPass startStateTrans)
>>= (\x -> >>= (\x ->
@ -369,13 +370,13 @@ testPassWithStateCheck testName expected actualPass startStateTrans checkFunc =
-- | A test that runs a given AST pass, checks that it succeeds, and checks the resulting 'CompState' and 'Items' with a given function. -- | A test that runs a given AST pass, checks that it succeeds, and checks the resulting 'CompState' and 'Items' with a given function.
testPassWithItemsStateCheck :: testPassWithItemsStateCheck ::
(Data a, Data b) => (Data a, Data b, TestMonad m r) =>
String -- ^ The test name. String -- ^ The test name.
-> a -- ^ The expected value. Can either be an actual AST, or a 'Pattern' to match an AST. -> a -- ^ The expected value. Can either be an actual AST, or a 'Pattern' to match an AST.
-> PassM b -- ^ The actual pass. -> PassM b -- ^ The actual pass.
-> (State CompState ()) -- ^ A function to transform a 'CompState'. Will be used on the 'emptyState' to get the initial state for the pass. -> (State CompState ()) -- ^ A function to transform a 'CompState'. Will be used on the 'emptyState' to get the initial state for the pass.
-> ((Items,CompState) -> Assertion) -- ^ A function to check the 'Items' and 'CompState' once the pass succeeds. -> ((Items,CompState) -> m ()) -- ^ A function to check the 'Items' and 'CompState' once the pass succeeds.
-> Assertion -> m ()
testPassWithItemsStateCheck testName expected actualPass startStateTrans checkFunc = testPassWithItemsStateCheck testName expected actualPass startStateTrans checkFunc =
(testPassGetItems testName expected actualPass startStateTrans) (testPassGetItems testName expected actualPass startStateTrans)
>>= (\x -> >>= (\x ->
@ -386,16 +387,16 @@ testPassWithItemsStateCheck testName expected actualPass startStateTrans checkFu
-- | A test that checks that a given AST pass fails. If the pass fails, the test succeeds. If the pass succeeds, the test fails. -- | A test that checks that a given AST pass fails. If the pass fails, the test succeeds. If the pass succeeds, the test fails.
testPassShouldFail :: testPassShouldFail ::
(Show b, Data b) => (Show b, Data b, TestMonad m r) =>
String -- ^ The test name. String -- ^ The test name.
-> PassM b -- ^ The actual pass. -> PassM b -- ^ The actual pass.
-> (State CompState ()) -- ^ A function to transform a 'CompState'. Will be used on the 'emptyState' to get the initial state for the pass. -> (State CompState ()) -- ^ A function to transform a 'CompState'. Will be used on the 'emptyState' to get the initial state for the pass.
-> Assertion -> m ()
testPassShouldFail testName actualPass startStateTrans = testPassShouldFail testName actualPass startStateTrans =
do ret <- runPass actualPass (execState startStateTrans emptyState) do ret <- runPass actualPass (execState startStateTrans emptyState)
case ret of case ret of
(_,Left err) -> return () (_,Left err) -> return ()
_ -> assertFailure $ testName ++ " pass succeeded when expected to fail, data: " ++ (pshow ret) _ -> testFailure $ testName ++ " pass succeeded when expected to fail, data: " ++ (pshow ret)
-- | Asserts that a particular variable is defined in the given 'CompState'. -- | Asserts that a particular variable is defined in the given 'CompState'.
assertVarDef :: assertVarDef ::

View File

@ -19,7 +19,7 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
module TreeUtils ( module TreeUtils (
MatchErrors, MatchErrors,
AnyDataItem(..), Items, castADI, AnyDataItem(..), Items, castADI,
assertPatternMatch, getMatchedItems, assertPatternMatch, testPatternMatch, getMatchedItems,
tag0, tag1, tag2, tag3, tag4, tag5, tag6, tag7, tag1d, tag2d, tag3d, tag4d, tag5d, tag6d, tag7d, tag0, tag1, tag2, tag3, tag4, tag5, tag6, tag7, tag1d, tag2d, tag3d, tag4d, tag5d, tag6d, tag7d,
(@@), (@@),
mkPattern, stopCaringPattern, namePattern, nameAndStopCaringPattern, mkPattern, stopCaringPattern, namePattern, nameAndStopCaringPattern,
@ -37,6 +37,7 @@ import Test.HUnit hiding (State)
import Pattern import Pattern
import qualified PrettyShow as PS import qualified PrettyShow as PS
import TestFramework
type MatchErrors = [String] type MatchErrors = [String]
@ -170,13 +171,13 @@ sequenceS x = (liftM concat) (sequence x)
-- | A function for checking that two Data items (expected, actual) match, where the expected item (LHS) -- | A function for checking that two Data items (expected, actual) match, where the expected item (LHS)
-- may contain special Pattern values (such as DontCare, Named, etc) -- may contain special Pattern values (such as DontCare, Named, etc)
assertPatternMatch :: (Data y, Data z) => String -> y -> z -> Assertion testPatternMatch :: (Data y, Data z, TestMonad m r) => String -> y -> z -> m ()
assertPatternMatch msg exp act = testPatternMatch msg exp act =
--Sometimes it can be hard to understand the MatchErrors as they stand. When you are told "1 expected, found 0" it's often hard --Sometimes it can be hard to understand the MatchErrors as they stand. When you are told "1 expected, found 0" it's often hard
--to know exactly which part of your huge match that refers to, especially if you can't see a 1 in your match. So to add a little --to know exactly which part of your huge match that refers to, especially if you can't see a 1 in your match. So to add a little
--bit of help, I append a pretty-printed version of the pattern and data to each error. --bit of help, I append a pretty-printed version of the pattern and data to each error.
sequence_ $ map ( sequence_ $ map (
assertFailure testFailure
. (append $ " while testing pattern:\n" ++ (PS.pshow exp) ++ "\n*** against actual:\n" ++ (PS.pshow act)) . (append $ " while testing pattern:\n" ++ (PS.pshow exp) ++ "\n*** against actual:\n" ++ (PS.pshow act))
. ((++) $ msg ++ " ") . ((++) $ msg ++ " ")
) errors ) errors
@ -184,6 +185,9 @@ assertPatternMatch msg exp act =
errors = evalState (checkMatch (mkPattern exp) act) (Map.empty) errors = evalState (checkMatch (mkPattern exp) act) (Map.empty)
append x y = y ++ x append x y = y ++ x
assertPatternMatch :: (Data y, Data z) => String -> y -> z -> Assertion
assertPatternMatch = testPatternMatch
-- | A function for getting the matched items from the patterns on the LHS -- | A function for getting the matched items from the patterns on the LHS
-- Either returns the matched items, or a list of errors from the matching -- Either returns the matched items, or a list of errors from the matching
getMatchedItems :: (Data y, Data z) => y -> z -> Either MatchErrors Items getMatchedItems :: (Data y, Data z) => y -> z -> Either MatchErrors Items