Added support for timing tests in the automatic test cases
This commit is contained in:
parent
d1fa9fd71f
commit
75dd2afeb2
|
@ -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]
|
||||||
|
|
Loading…
Reference in New Issue
Block a user