packages feed

ideas-1.0: src/Documentation/SelfCheck.hs

-----------------------------------------------------------------------------
-- Copyright 2011, 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 (selfCheck, blackBoxTests) where

import Common.Exercise
import Common.Utils (useFixedStdGen, Some(..), snd3)
import Common.Utils.TestSuite
import Control.Monad
import Data.List
import Service.DomainReasoner
import Service.ModeJSON
import Service.ModeXML
import Service.Request
import System.Directory
import qualified Common.Algebra.Boolean as Algebra
import qualified Common.Algebra.Field as Algebra
import qualified Common.Rewriting.Substitution as Substitution
import qualified Common.Rewriting.Unification as Unification
import qualified Common.Strategy.Tests as Strategy
import qualified Text.JSON as JSON
import qualified Text.OpenMath.Tests as OpenMath
import qualified Text.UTF8 as UTF8

selfCheck :: String -> DomainReasoner TestSuite
selfCheck dir = do
   list        <- getExercises
   domainSuite <- getTestSuite
   run         <- runWithCurrent

   return $ do
      suite "Framework checks" $ do
         suite "Text encodings" $ do
            addProperty "UTF8 encoding" UTF8.propEncoding
            addProperty "JSON encoding" JSON.propEncoding
            addProperty "OpenMath encoding" OpenMath.propEncoding
         Substitution.tests
         Unification.unificationTests
         Strategy.tests
         suite "Field properties" $
            mapM_ (addProperty "field") Algebra.propsField
         suite "Boolean properties" $
            mapM_ (addProperty "boolean") Algebra.propsBoolean

      suite "Domain checks" domainSuite

      suite "Exercise checks" $
         forM_ list $ \(Some ex) ->
            exerciseTestSuite ex

      suite "Black box tests" $
         join (liftIO (blackBoxTests run dir))

-- Returns the number of tests performed
blackBoxTests :: (DomainReasoner Bool -> IO Bool) -> String -> IO TestSuite
blackBoxTests run path = do
   putStrLn ("Scanning " ++ path)
   -- analyse content
   xs0 <- getDirectoryContents path
   let (xml,  xs1) = partition (".xml"  `isSuffixOf`) xs0
       (json, xs2) = partition (".json" `isSuffixOf`) xs1
   -- perform tests
   ts1 <- forM json $ \x ->
             doBlackBoxTest run JSON (path ++ "/" ++ x)
   ts2 <- forM xml $ \x ->
             doBlackBoxTest run XML (path ++ "/" ++ x)
   -- recursively visit subdirectories
   ts3 <- forM (filter ((/= ".") . take 1) xs2) $ \x -> do
             let p = path ++ "/" ++ x
             valid <- doesDirectoryExist p
             if not valid
                then return (return ())
                else liftM (suite $ "Directory " ++ simplerDirectory p)
                           (blackBoxTests run p)
   return $
      sequence_ (ts1 ++ ts2 ++ ts3)

doBlackBoxTest :: (DomainReasoner Bool -> IO Bool) -> DataFormat -> FilePath -> IO TestSuite
doBlackBoxTest run format path = do
   b <- doesFileExist expPath
   return $ if not b
      then warn $ expPath ++ " does not exist"
      else assertIO (stripDirectoryPart path) $ run $ do
         -- Comparing output with expected output
         (txt, expt) <- liftIO $ do
            useFixedStdGen -- fix the random number generator
            txt  <- readFile path
            expt <- liftIO $ readFile expPath
            return (txt, expt)
         out  <- case format of
                    JSON -> liftM snd3 (processJSON txt)
                    XML  -> liftM snd3 (processXML txt)
         -- Conditional forces evaluation of the result, to make sure that
         -- all file handles are closed afterwards.
         if out ~= expt then return True else return False
       `catchError`
         \_ -> return False
 where
   expPath = baseOf path ++ ".exp"
   baseOf  = reverse . drop 1 . dropWhile (/= '.') . reverse
   x ~= y  = filterVersion x == filterVersion y -- compare line-based

   filterVersion =
      let p s = not (null s || "version" `isInfixOf` s)
      in filter p . lines . filter (/= '\r')

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 ]
-}