CC/tester/KompTest.hs
Jeena Paradies 063194f8be first commit
2011-04-19 11:37:05 +02:00

299 lines
8.4 KiB
Haskell

-- {-# LANGUAGE NamedFieldPuns, ScopedTypeVariables #-}
-- GHC needs -threaded
module KompTest where
import Control.Monad
import Data.List
import Data.Maybe
import System
import System.Directory
import System.Environment
import System.Exit
import System.IO
import System.FilePath
import Data.Char
import RunCommand
data Backend = Backend {
name :: String,
objFile :: FilePath -> FilePath,
run :: String -> FilePath -> FilePath -> FilePath -> IO Bool
}
--
-- * Error reporting and output checking
--
reportErrorColor :: Color
-> String -- ^ command that failed
-> String -- ^ how it failed
-> FilePath -- ^ source file
-> String -- ^ given input
-> String -- ^ stdout output
-> String -- ^ stderr output
-> IO ()
reportErrorColor col c m f i o e =
do
putStrLn $ color col $ c ++ " failed: " ++ m
putStrLn $ "For source file " ++ f ++ ":"
-- prFile f
when (not (null i)) $ do
putStrLn "Given this input:"
putStrLn $ color blue $ i
when (not (null o)) $ do
putStrLn "It printed this to standard output:"
putStrLn $ color blue $ o
when (not (null e)) $ do
putStrLn "It printed this to standard error:"
putStrLn $ color blue $ e
reportError :: String -- ^ command that failed
-> String -- ^ how it failed
-> FilePath -- ^ source file
-> String -- ^ given input
-> String -- ^ stdout output
-> String -- ^ stderr output
-> IO ()
reportError = reportErrorColor red
data ErrorReport = ErrorReport {
repErr :: String,
repSeverity :: Severity,
repCmd :: String,
repStdIn :: String,
repStdOut :: String,
repStdErr :: String
}
data Severity = SWarning | SError | SInfo
defRep = ErrorReport {
repErr = "OK",
repSeverity = SInfo,
repCmd = "",
repStdIn = "",
repStdOut = "",
repStdErr = ""
}
rep ?! msg = rep { repErr = msg, repSeverity = SError }
rep ?? msg = rep { repErr = msg, repSeverity = SWarning }
report0 er = case repSeverity er of
SInfo -> return True
_ -> do reportError (repCmd er) (repErr er) (repCmd er) (repStdIn er) (repStdOut er) (repStdErr er)
return False
report1 Nothing = return True
report1 (Just rep) = report0 rep >> return False
prFile :: FilePath -> IO ()
prFile f = do
putStrLn $ "---------------- begin " ++ f ++ " ------------------"
s <- readFile f
putStrLn $ color green s
putStrLn $ "----------------- end " ++ f ++ " -------------------"
-- | Report how many tests passed.
report :: String -> [Bool] -> IO ()
report n rs =
do let (p,t) = (length (filter id rs), length rs)
putStrLn $ n ++ ": passed " ++ show p ++ " of " ++ show t ++ " tests"
--
-- * Generic running
--
runProg :: String -- ^ command
-> FilePath -- ^ source file (for error reporting)
-> FilePath -- ^ known input file
-> FilePath -- ^ known output file
-> IO Bool
runProg c f i o =
do
fe <- doesFileExist i
input <- if fe then readFile i else return ""
output <- readFile o
(out,err,s) <- runCommandStrWait c input
case s of
ExitFailure x -> do
reportError c ("with status " ++ show x) f input out err
return False
ExitSuccess ->
do
if not (null err) then
do
reportError c "Printed something to standard error" f input out err
return False
else if output /= out then
do
putStrLn $ color red $ c ++ " produced the wrong output:"
putStrLn $ "For source file " ++ f ++ ":"
prFile f
when (not (null input)) $ do
putStrLn "Given this input:"
putStrLn $ color blue $ input
putStrLn "It printed this to standard output:"
putStrLn $ color blue $ out
putStrLn "It should have printed this:"
putStrLn $ color blue $ output
return False
else do
putStrLn "output ok"
return True
testProg :: FilePath -- ^ executable
-> Backend
-> FilePath -- ^ test file
-> IO Bool
testProg cmd b f = do
putStr $ "Testing " ++ takeFileName f ++ ": "
hFlush stdout
let n = dropExtension f
o = n ++ ".output"
let c = objFile b f
ofe <- doesFileExist o
if ofe then run b c f (n ++ ".input") o
else do
putStrLn $ color blue $ "skipping: " ++ o ++ " not found"
return True
test :: FilePath -> [FilePath] -> Backend -> IO [Bool]
test c fs b =
do putStrLn $ color green $ "Backend: " ++ name b
mapM (testProg c b) fs
--
-- * Compilation
--
testCompilation :: FilePath -> Bool -> [FilePath] -> IO [Bool]
testCompilation c good fs =
do x <- doesFileExist c
forM fs $ \t -> report0 =<< do
if x then testCompilationProg c good t
else return $ defRep {repCmd = c} ?! ("compiler " ++ c ++ " not found")
testCompilationProg :: FilePath -> Bool -> FilePath -> IO ErrorReport
testCompilationProg path good f =
do let c = path ++ " " ++ f
putStrLn $ takeFileName path ++ " " ++ takeFileName f ++ "..."
(out,err,_) <- runCommandStrWait c ""
let rep = defRep {repCmd = f, repStdOut = out, repStdErr = err}
lns <- return $ lines err
return $ case filter (not . null) lns of
msgs | isOk msgs -> if good then rep else rep ?! "passed BAD program"
| isError msgs -> if good then rep ?! "failed OK program" else rep
_ -> rep ?! "invalid output"
where isOk (s:_) | "OK" `isSuffixOf` tu s || "OK" `isPrefixOf` tu s = True
isOk _ = False
isError (s:_) | "ERROR" `isSuffixOf` tu s || "ERROR" `isPrefixOf` tu s = True
isError ("Syntax Error, trying to recover and continue parse...":"ERROR":_) = True
isError _ = False
tu = map toUpper
--
-- * JVM back-end
--
objFileJVM f = dropExtension f
runJVM :: String -- libpath
-> String -- ^ Java class file
-> FilePath -- ^ source file (for error reporting)
-> FilePath -- ^ known input file
-> FilePath -- ^ known output file
-> IO Bool
runJVM libPath classFile src inp outp = do
let dir = takeDirectory classFile
d0 <- System.Directory.getCurrentDirectory
setCurrentDirectory dir
result <- runProg ("java -cp .:" ++ libPath ++ " " ++ takeBaseName classFile) src inp outp
setCurrentDirectory d0
return result
jvmBackend libpath = Backend { name = "JVM", objFile = objFileJVM,
run = runJVM libpath }
--
-- * LLVM back-end
--
objFileLLVM f = dropExtension f <.> "bc"
runLLVM :: String -- libpath
-> String -- ^ LLVM bitcode file
-> FilePath -- ^ source file (for error reporting)
-> FilePath -- ^ known input file
-> FilePath -- ^ known output file
-> IO Bool
runLLVM libPath bcFile src inp outp = do
let dir = takeDirectory bcFile
d0 <- System.Directory.getCurrentDirectory
setCurrentDirectory dir
system ("rm a.out")
system ("llvm-ld " ++ bcFile ++ " " ++ libPath ++"/runtime.bc")
result <- runProg "./a.out" src inp outp
setCurrentDirectory d0
return result
llvmBackend libpath = Backend { name = "LLVM", objFile = objFileLLVM,
run = runLLVM libpath }
--
-- * x86 back-end
--
objFilex86 f = dropExtension f <.> "o"
runx86 libPath oFile src inp outp = do
let dir = takeDirectory oFile
d0 <- System.Directory.getCurrentDirectory
setCurrentDirectory dir
system ("rm a.out")
system ("gcc -m32 " ++ oFile ++ " " ++ libPath ++"/runtime.o")
result <- runProg "./a.out" src inp outp
setCurrentDirectory d0
return result
x86Backend libpath = Backend { name = "x86", objFile = objFilex86,
run = runx86 libpath }
getTestFilesForPath :: String -> IO [String]
getTestFilesForPath f = do
d <- doesDirectoryExist f
if d then jlFiles f else do
d' <- doesFileExist f
if d' then return [f]
else error $ "Not a file or directory: " ++ f
-- | Get all .jl files in the given directory.
jlFiles :: FilePath -> IO [FilePath]
jlFiles d = do fs <- getDirectoryContents d
return $ map (d </>) $ sort $ filter ((".jl" ==) . takeExtension) fs
--
-- * Terminal output colors
--
type Color = Int
color :: Color -> String -> String
color c s = fgcol c ++ s ++ normal
highlight = "\ESC[7m"
bold = "\ESC[1m"
underline = "\ESC[4m"
normal = "\ESC[0m"
fgcol col = "\ESC[0" ++ show (30+col) ++ "m"
bgcol col = "\ESC[0" ++ show (40+col) ++ "m"
red, green, blue :: Color
red = 1
green = 2
blue = 4