Added support for timing tests in the automatic test cases

This commit is contained in:
Neil Brown 2008-01-23 15:23:26 +00:00
parent d1fa9fd71f
commit 75dd2afeb2

View File

@ -24,17 +24,19 @@ There is an initial prologue, assumed to be source code. Any lines beginning wi
to be destinations for substitution. The prologue is terminated by the first test. to be destinations for substitution. The prologue is terminated by the first test.
Each test has a starting line that begins with a single % followed by either PASS or FAIL, Each test has a starting line that begins with a single % followed by either PASS or FAIL,
optionally followed by an * (indicating this test is a good benchmark for timing)
followed by a test name. The lines that run to the start of the next test are the source substitution. followed by a test name. The lines that run to the start of the next test are the source substitution.
The file is terminated by a single percent on its own line. The file is terminated by a single percent on its own line.
-} -}
module TestHarness (automaticTest) where module TestHarness (automaticTest, automaticTimeTest) where
import Control.Monad.Error import Control.Monad.Error
import Control.Monad.State import Control.Monad.State
import Data.List import Data.List
import Data.Maybe
import System.IO import System.IO
import Test.HUnit hiding (performTest) import Test.HUnit hiding (performTest)
import Text.Regex import Text.Regex
@ -44,11 +46,15 @@ import ParseOccam
import Pass import Pass
import PassList import PassList
import PreprocessOccam import PreprocessOccam
import TestUtils
import Utils import Utils
automaticTest :: FilePath -> IO Test automaticTest :: FilePath -> IO Test
automaticTest fileName = readFile fileName >>* performTest fileName automaticTest fileName = readFile fileName >>* performTest fileName
automaticTimeTest :: (Int, Int, Int) -> FilePath -> IO [TimedTask]
automaticTimeTest scale fileName = readFile fileName >>* performTimeTest scale fileName
-- Bit of a hard-hack, until usage-checking is on by default: -- Bit of a hard-hack, until usage-checking is on by default:
defaultState :: CompState defaultState :: CompState
defaultState = emptyState {csUsageChecking = True} defaultState = emptyState {csUsageChecking = True}
@ -62,6 +68,12 @@ testOccam source = do result <- evalStateT (runErrorT compilation) defaultState
Right _ -> Nothing Right _ -> Nothing
where where
compilation = preprocessOccamSource source >>= parseOccamProgram >>= runPasses (getPassList defaultState) compilation = preprocessOccamSource source >>= parseOccamProgram >>= runPasses (getPassList defaultState)
-- | Substitutes each substitution into the prologue
substitute :: String -> [(Bool, Bool, String, String)] -> [(Bool, Bool, String, String)]
substitute prologue = map (\(a,b,c,subst) -> (a,b,c,subRegex (mkRegex "%%") prologue subst))
-- | Given a file's contents, tests it -- | Given a file's contents, tests it
performTest :: String -> String -> Test performTest :: String -> String -> Test
performTest fileName fileContents performTest fileName fileContents
@ -70,33 +82,42 @@ performTest fileName fileContents
Right (prologue,tests) -> TestLabel fileName $ TestList $ map performTest' (substitute prologue tests) Right (prologue,tests) -> TestLabel fileName $ TestList $ map performTest' (substitute prologue tests)
where where
-- Substitutes each substitution into the prologue performTest' :: (Bool, Bool, String, String) -> Test
substitute :: String -> [(Bool, String, String)] -> [(Bool, String, String)] performTest' (expPass, _, testName, testText)
substitute prologue = map (\(a,b,subst) -> (a,b,subRegex (mkRegex "%%") prologue subst))
performTest' :: (Bool, String, String) -> Test
performTest' (expPass, testName, testText)
= TestCase $ = TestCase $
do result <- testOccam testText do result <- testOccam testText
case result of case result of
Just err -> if expPass then assertFailure (testName ++ " failed with error: " ++ err) else return () Just err -> if expPass then assertFailure (testName ++ " failed with error: " ++ err) else return ()
Nothing -> if expPass then return () else assertFailure (testName ++ " expected to fail but passed") Nothing -> if expPass then return () else assertFailure (testName ++ " expected to fail but passed")
performTimeTest :: (Int, Int, Int) -> String -> String -> [TimedTask]
performTimeTest scale fileName fileContents
= case parseTestFile fileContents of
Left err -> error $ "Error processing file \"" ++ fileName ++ "\": " ++ err
Right (prologue,tests) -> mapMaybe performTimeTest' (substitute prologue tests)
where
performTimeTest' :: (Bool, Bool, String, String) -> Maybe TimedTask
performTimeTest' (_, False, _, _) = Nothing
performTimeTest' (_, True, testName, testText) = Just $ timeTask testName scale (testOccam testText >> return ())
-- | Splits a file's contents into the prologue, and subsequent testcases -- | Splits a file's contents into the prologue, and subsequent testcases
parseTestFile :: String -> Either String (String, [(Bool, String, String)]) parseTestFile :: String -> Either String (String, [(Bool, Bool, String, String)])
parseTestFile wholeText = seqPair (return $ unlines prologue, splitCases testcases) parseTestFile wholeText = seqPair (return $ unlines prologue, splitCases testcases)
where where
allLines = lines wholeText allLines = lines wholeText
(prologue, testcases) = span (\l -> ("%%" `isPrefixOf` l) || (not $ "%" `isPrefixOf` l)) allLines (prologue, testcases) = span (\l -> ("%%" `isPrefixOf` l) || (not $ "%" `isPrefixOf` l)) allLines
splitCases :: [String] -> Either String [(Bool, String, String)] splitCases :: [String] -> Either String [(Bool, Bool, String, String)]
splitCases [] = throwError "Unexpected EOF" splitCases [] = throwError "Unexpected EOF"
splitCases (headLine:otherLines) splitCases (headLine:otherLines)
| "%PASS" `isPrefixOf` headLine = joinM (True, drop 5 headLine , unlines testCaseLines) (splitCases furtherLines) | "%PASS" `isPrefixOf` headLine = joinM (True, hasStar, testTitle, unlines testCaseLines) (splitCases furtherLines)
| "%FAIL" `isPrefixOf` headLine = joinM (False, drop 5 headLine , unlines testCaseLines) (splitCases furtherLines) | "%FAIL" `isPrefixOf` headLine = joinM (False, hasStar, testTitle, unlines testCaseLines) (splitCases furtherLines)
| "%" == headLine = return [] | "%" == headLine = return []
| otherwise = throwError $ "Unexpected format in testcase-header line: \"" ++ headLine ++ "\"" | otherwise = throwError $ "Unexpected format in testcase-header line: \"" ++ headLine ++ "\""
where where
(hasStar, testTitle) = if headLine !! 5 == '*' then (True, drop 6 headLine) else (False, drop 5 headLine)
(testCaseLines, furtherLines) = span (not . isPrefixOf "%") otherLines (testCaseLines, furtherLines) = span (not . isPrefixOf "%") otherLines
joinM :: Monad m => a -> m [a] -> m [a] joinM :: Monad m => a -> m [a] -> m [a]