ideas-0.6: src/Documentation/SelfCheck.hs
-----------------------------------------------------------------------------
-- Copyright 2010, Open Universiteit Nederland. This file is distributed
-- under the terms of the GNU General Public License. For more information,
-- see the file "LICENSE.txt", which is included in the distribution.
-----------------------------------------------------------------------------
-- |
-- Maintainer : bastiaan.heeren@ou.nl
-- Stability : provisional
-- Portability : portable (depends on ghc)
--
-----------------------------------------------------------------------------
module Documentation.SelfCheck (performSelfCheck) where
import Control.Monad.Trans
import System.Directory
import Common.Utils (reportTest, useFixedStdGen, Some(..), snd3)
import Common.Exercise
import Service.ExercisePackage
import qualified Common.Strategy.Grammar as Grammar
import Control.Monad
import Service.Request
import Service.DomainReasoner
import qualified Domain.LinearAlgebra.Checks as LA
import Service.ModeJSON
import Service.ModeXML
import qualified Domain.Math.Numeric.Tests as MathNum
import qualified Domain.Math.Polynomial.Tests as MathPoly
import qualified Domain.Math.SquareRoot.Tests as MathSqrt
import qualified Domain.Math.Data.Interval as MathInterval
import qualified Text.UTF8 as UTF8
import qualified Text.JSON as JSON
import Data.List
import System.Time
performSelfCheck :: String -> DomainReasoner ()
performSelfCheck dir = totalDiff $ do
timeDiff $ liftIO $ do
putStrLn "* 1. Domain checks"
Grammar.checks
MathNum.main
MathPoly.tests
MathSqrt.tests
MathInterval.testMe
LA.checks
UTF8.testEncoding
JSON.testMe
liftIO $ putStrLn "* 2. Exercise checks"
pkgs <- getPackages
forM_ pkgs $ \(Some pkg) ->
timeDiff $ liftIO $ checkExercise (exercise pkg)
timeDiff $ do
liftIO $ putStrLn "* 3. Unit tests"
n <- unitTests dir
liftIO $ putStrLn $ "** Number of unit tests: " ++ show n
-- Returns the number of tests performed
unitTests :: String -> DomainReasoner Int
unitTests = visit 0
where
visit i path = do
valid <- liftIO $ doesDirectoryExist path
if not valid then return 0 else do
-- analyse content
xs <- liftIO $ getDirectoryContents path
let xml = filter (".xml" `isSuffixOf`) xs
json = filter (".json" `isSuffixOf`) xs
liftIO $ putStrLn $ replicate (i+1) '*' ++ " " ++ simplerDirectory path
-- perform tests
forM json $ \x ->
performUnitTest JSON (path ++ "/" ++ x)
forM xml $ \x ->
performUnitTest XML (path ++ "/" ++ x)
-- recursively visit subdirectories
is <- forM (filter ((/= ".") . take 1) xs) $ \x ->
visit (i+1) (path ++ "/" ++ x)
return (length (xml ++ json) + sum is)
performUnitTest :: DataFormat -> FilePath -> DomainReasoner ()
performUnitTest format path = do
liftIO useFixedStdGen -- fix the random number generator
txt <- liftIO $ readFile path
exp <- liftIO $ readFile expPath
out <- case format of
JSON -> liftM snd3 (processJSON txt)
XML -> liftM snd3 (processXML txt)
`catchError` \_ -> return "Error"
liftIO $ reportTest (stripDirectoryPart path) (out ~= exp)
where
expPath = baseOf path ++ ".exp"
baseOf = reverse . drop 1 . dropWhile (/= '.') . reverse
x ~= y = filterVersion x == filterVersion y
filterVersion =
let p s = not (null s || "version" `isInfixOf` s)
in unlines . filter p . lines
simplerDirectory :: String -> String
simplerDirectory s
| "../" `isPrefixOf` s = simplerDirectory (drop 3 s)
| "test/" `isPrefixOf` s = simplerDirectory (drop 5 s)
| otherwise = s
stripDirectoryPart :: String -> String
stripDirectoryPart = reverse . takeWhile (/= '/') . reverse
{-
logicConfluence :: IO ()
logicConfluence = reportTest "logic rules" (isConfluent f rs)
where
f = normalizeWith ops . normalFormWith ops rs
ops = map makeCommutative Logic.logicOperators
rwrs = Logic.logicRules \\ [Logic.ruleOrOverAnd, Logic.ruleCommOr, Logic.ruleCommAnd]
rs = [ r | RewriteRule r <- concatMap transformations rwrs ]
-- eqs = bothWays [ r | RewriteRule r <- concatMap transformations Logic.logicRules ]
-}
-- Helper functions
showDiffWith :: MonadIO m => (TimeDiff -> IO ()) -> m a -> m a
showDiffWith f action = do
t0 <- liftIO getClockTime
a <- action
t1 <- liftIO getClockTime
liftIO (f (diffClockTimes t1 t0))
return a
totalDiff :: MonadIO m => m a -> m a
totalDiff = showDiffWith (putStrLn . ("*** Total time: "++) . formatDiff)
timeDiff :: MonadIO m => m a -> m a
timeDiff = showDiffWith (putStrLn . ("+++ Time: "++) . formatDiff)
formatDiff :: TimeDiff -> String
formatDiff d@(TimeDiff z1 z2 z3 h m s p)
| any (/=0) [z1,z2,z3] = timeDiffToString d
| s >= 60 = formatDiff (timeDiff ((h*60+m)*60+s) p)
| h==0 && m==0 = show inSec ++ " secs"
| otherwise = show (60*h+m) ++ ":" ++ digSec ++ " mins"
where
milSec = 1000*toInteger s + p `div` 1000000000
inSec = fromIntegral milSec / 1000
digSec = (if s < 10 then ('0' :) else id) (show s)
timeDiff n p =
let (rest, s) = n `divMod` 60
(h, m) = rest `divMod` 60
in TimeDiff 0 0 0 h m s p