-------------------------------------------------------------------------------- -- Test Suite for Curry Base -------------------------------------------------------------------------------- -- -- This Test Suite supports three kinds of tests: -- -- 1) tests which should pass -- 2) tests which should pass with a specific warning -- 3) tests which should fail yielding a specific error message -- -- In order to add a test to this suite, proceed as follows: -- -- 1) Store your test code in a file (please use descriptive names) and put it -- in the corresponding subfolder (i.e. test/pass for passing tests, -- test/fail for failing tests and test/warning for passing tests producing -- warnings) -- 2) Extend the corresponding test information list (there is one for each test -- group at the end of this file) with the required information (i.e. name of -- the Curry module to be tested and expected warning/failure message(s)) -- 3) Run 'cabal test' {-# LANGUAGE CPP #-} module TestBase (tests) where #if __GLASGOW_HASKELL__ < 710 import Control.Applicative ((<$>)) #endif import Control.Monad.Trans (lift) import Data.List (isInfixOf, sort) import Distribution.TestSuite import System.FilePath (FilePath, (), (<.>)) import Curry.Base.Message (Message, ppMessages, ppError) import Curry.Base.Monad (CYIO, runCYIO, liftCYM) import Curry.Files.PathUtils (readModule) import Curry.Syntax (parseModule, unlit) tests :: IO [Test] tests = return [passingTests, warningTests, failingTests] -- Call the Curry parser parseCurry :: FilePath -> CYIO () parseCurry file = do msrc <- lift $ lift $ readModule file case msrc of Nothing -> error $ "Missing file " ++ file Just src -> liftCYM $ do ul <- unlit file src parseModule file ul return () -- Execute a test by calling cymake runTest :: String -> [String] -> IO Progress runTest test [] = runCYIO (parseCurry test) >>= passOrFail where passOrFail = (Finished <$>) . either fail pass fail msgs | null msgs = return Pass | otherwise = let errorStr = showMessages msgs in return $ Fail $ "An unexpected failure occurred: " ++ errorStr pass _ = return Pass runTest test errorMsgs = runCYIO (parseCurry test) >>= catchE where catchE = (Finished <$>) . either pass fail pass msgs = let errorStr = showMessages msgs in if all (`isInfixOf` errorStr) errorMsgs then return Pass else return $ Fail $ "Expected warning/failure did not occur: " ++ errorStr fail = pass . snd showMessages :: [Message] -> String showMessages = show . ppMessages ppError . sort -- group of tests which should pass passingTests :: Test passingTests = Group { groupName = "Passing Tests" , concurrently = False , groupTests = map (mkTest "test/pass/") passInfos } -- group of test which should fail yielding a specific error message failingTests :: Test failingTests = Group { groupName = "Failing Tests" , concurrently = False , groupTests = map (mkTest "test/fail/") failInfos } -- group of tests which should pass producing a specific warning message warningTests :: Test warningTests = Group { groupName = "Warning Tests" , concurrently = False , groupTests = map (mkTest "test/warning/") warnInfos } -- create a new test mkTest :: FilePath -> TestInfo -> Test mkTest path (testName, testTags, testOpts, mSetOpts, errorMsgs) = let file = path testName <.> "curry" test = TestInstance { run = runTest file errorMsgs , name = testName , tags = testTags , options = testOpts , setOption = maybe (\_ _ -> Right test) id mSetOpts } in Test test -- Information for a test instance: -- * name of test -- * tags to classify a test -- * options -- * function to set options -- * optional warning/error message which should be thrown on execution of test type TestInfo = (String, [String], [OptionDescr], Maybe SetOption, [String]) type SetOption = String -> String -> Either String TestInstance -------------------------------------------------------------------------------- -- Definition of passing tests -------------------------------------------------------------------------------- -- generate a simple passing test mkPassTest :: String -> TestInfo mkPassTest name = (name, [], [], Nothing, []) -- To add a passing test to the test suite simply add the module name of the -- test code to the following list -- TODO: add test cases passInfos :: [TestInfo] passInfos = map mkPassTest [] -------------------------------------------------------------------------------- -- Definition of failing tests -------------------------------------------------------------------------------- -- generate a simple failing test mkFailTest :: String -> [String] -> TestInfo mkFailTest name errorMsgs = (name, [], [], Nothing, errorMsgs) -- To add a failing test to the test suite simply add the module name of the -- test code and the expected error message(s) to the following list -- TODO: add test cases failInfos :: [TestInfo] failInfos = map (uncurry mkFailTest) [] -------------------------------------------------------------------------------- -- Definition of warning tests -------------------------------------------------------------------------------- -- To add a warning test to the test suite simply add the module name of the -- test code and the expected warning message(s) to the following list -- TODO: add test cases warnInfos :: [TestInfo] warnInfos = map (uncurry mkFailTest) []