{-# LANGUAGE BangPatterns #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} module Main where import Control.Exception import Control.Monad import Data.Time.Clock import Data.Time.Format import Debug.Trace import System.Directory import System.Environment import System.Exit import System.FilePath import System.IO -- import System.IO.Temp import Test.Common import Test.CommonUtils import Test.HUnit import qualified Data.Set as S -- --------------------------------------------------------------------- data Verbosity = Debug | Status | None deriving (Eq, Show, Ord, Enum) verb :: Verbosity verb = Debug -- --------------------------------------------------------------------- writeCPP :: FilePath -> IO () writeCPP fp = appendFileFlush cppFile (('\n' : fp)) writeError :: FilePath -> IO () writeError = writeCPP writeParseFail :: FilePath -> String -> IO () writeParseFail fp _s = appendFileFlush parseFailFile (('\n' : fp)) -- writeParseFail fp s = appendFileFlush parseFailFile (('\n' : (fp ++ " " ++ s))) writeProcessed :: FilePath -> IO () writeProcessed fp = appendFileFlush processed (('\n' : fp)) writeFailed :: FilePath -> IO () writeFailed fp = appendFileFlush processedFailFile (('\n' : fp)) writeLog :: String -> IO () writeLog msg = appendFileFlush logFile (('\n' : msg)) getTimeStamp :: IO String getTimeStamp = do t <- getCurrentTime return $ formatTime defaultTimeLocale (iso8601DateFormat (Just "%H%M%S")) t writeFailure :: FilePath -> String -> IO () writeFailure fp db = do ts <- getTimeStamp let outname = failuresDir takeFileName fp <.> ts <.> "out" writeFile outname db appendFileFlush :: FilePath -> String -> IO () appendFileFlush f txt = withFile f AppendMode (\ hdl -> hPutStr hdl txt >> hFlush hdl) -- --------------------------------------------------------------------- readFileIfPresent :: FilePath -> IO [String] readFileIfPresent fileName = do isPresent <- doesFileExist fileName if isPresent then lines <$> readFile fileName else return [] -- --------------------------------------------------------------------- main :: IO () main = do createDirectoryIfMissing True workDir createDirectoryIfMissing True configDir createDirectoryIfMissing True failuresDir as <- getArgs case as of [] -> putStrLn "Must enter directory to process" ["failures"] -> do fs <- lines <$> readFile origFailuresFile () <$ runTests (TestList (map mkParserTest fs)) ["clean"] -> do putStrLn "Cleaning..." writeFile processed "" writeFile parseFailFile "" writeFile cppFile "" writeFile logFile "" writeFile processedFailFile "" removeDirectoryRecursive failuresDir createDirectory failuresDir putStrLn "Done." -- ds -> () <$ (runTests =<< (TestList <$> mapM tests ds)) ds -> do !blackList <- readFileIfPresent blackListed !knownFailures <- readFileIfPresent knownFailuresFile !processedList <- lines <$> readFile processed !cppList <- lines <$> readFile cppFile !parseFailList <- lines <$> readFile parseFailFile let done = S.fromList (processedList ++ cppList ++ blackList ++ knownFailures ++ parseFailList) tsts <- TestList <$> mapM (tests done) ds _ <- runTests tsts return () runTests :: Test -> IO Counts runTests t = do let n = testCaseCount t putStrLn $ "Running " ++ show n ++ " tests." putStrLn $ "Verbosity: " ++ show verb runTestTT t tests :: S.Set String -> FilePath -> IO Test tests done dir = do roundTripHackage done dir -- Selection: -- Hackage dir roundTripHackage :: S.Set String -> FilePath -> IO Test roundTripHackage done hackageDir = do packageDirs <- drop 2 <$> getDirectoryContents hackageDir when (verb <= Debug) (traceShowM hackageDir) when (verb <= Debug) (traceShowM packageDirs) TestList <$> mapM (roundTripPackage done) (zip [0..] (map (hackageDir ) packageDirs)) roundTripPackage :: S.Set String -> (Int, FilePath) -> IO Test roundTripPackage done (n, dir) = do putStrLn (show n) when (verb <= Status) (traceM dir) hsFiles <- filter (flip S.notMember done) <$> findSrcFiles dir return (TestLabel (dropFileName dir) (TestList $ map mkParserTest hsFiles)) mkParserTest :: FilePath -> Test mkParserTest fp = TestLabel fp $ TestCase (do writeLog $ "starting:" ++ fp r1 <- catchAny (roundTripTest fp) $ \e -> do writeError fp throwIO e case r1 of Left (ParseFailure s) -> do writeParseFail fp s exitFailure Right r -> do writeProcessed fp unless (status r == Success) (writeFailure fp (debugTxt r) >> writeFailed fp) assertBool fp (status r == Success)) catchAny :: IO a -> (SomeException -> IO a) -> IO a catchAny = Control.Exception.catch