--getDisplay
--defs
module
Test.Chuchu
(chuchuMain, module Test.Chuchu.Types, module Test.Chuchu.Parser)
where
import Control.Applicative ((<$>), Applicative((<*>)))
import Control.Monad (unless)
import Control.Monad.IO.Class (MonadIO(liftIO))
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.Control (MonadBaseControl)
import Control.Monad.Trans.Reader (ReaderT(..), ask)
import Data.Either (partitionEithers)
import Language.Abacate hiding (StepKeyword (..))
import System.Console.CmdArgs
import System.Environment (getProgName)
import System.Exit (exitFailure, exitWith, ExitCode(ExitFailure))
import qualified Control.Exception.Lifted as E
import qualified Data.IORef as I
import qualified Text.Parsec as P
import Test.Chuchu.Types
import Test.Chuchu.Parser
import Test.Chuchu.OutputPrinter
chuchuMain :: (MonadBaseControl IO m, MonadIO m, Applicative m) =>
Chuchu m -> (m () -> IO ()) -> IO ()
chuchuMain stepDefinitions runMIO = do
listOfPaths <- getPaths
parsedFiles <- mapM parseFile listOfPaths
let result = partitionEithers parsedFiles
case result of
([], filesToExecute) -> do
rets <- concat <$> mapM (processAbacate stepDefinitions runMIO) filesToExecute
let n = length $ filter not rets
unless (n == 0) $ exitWith (ExitFailure (min 255 n))
(filesWithError, _) -> do
warn "Could not parse the following files: "
mapM_ (warn . flip (++) "\n" . show) filesWithError
exitFailure
data ExecutionPlan =
ExecutionPlan
{ epBackground :: Maybe Background
, epScenario :: FeatureElement
}
deriving (Show)
createExecutionPlans :: Abacate -> [ExecutionPlan]
createExecutionPlans feature =
ExecutionPlan (fBackground feature) `map` fFeatureElements feature
type Execution m a = ReaderT (ParseStep m) m a
type ParseStep m = Step -> Either P.ParseError (m ())
runExecution :: (MonadIO m, Applicative m) =>
Chuchu m -> (m () -> IO ()) -> Execution m () -> IO ()
runExecution stepDefinitions runMIO act = runMIO $ runReaderT act parseStep
where parseStep = P.parse (runChuchu stepDefinitions) "Step definitions" . stBody
processAbacate :: (MonadBaseControl IO m, MonadIO m, Applicative m) =>
Chuchu m
-> (m () -> IO ())
-> Abacate
-> IO [Bool]
processAbacate stepDefinitions runMIO feature = do
putDoc $ describeAbacate feature
let plans = createExecutionPlans feature
retVar <- liftIO $ I.newIORef []
let addRet ret = liftIO $ I.modifyIORef retVar (ret:)
mapM_ (runExecution stepDefinitions runMIO . (>>= addRet) . processExecutionPlan) plans
reverse <$> liftIO (I.readIORef retVar)
processExecutionPlan :: (MonadBaseControl IO m, MonadIO m, Applicative m) =>
ExecutionPlan -> Execution m Bool
processExecutionPlan (ExecutionPlan mbackground scenario) = do
liftIO $ putStrLn ""
(&&) <$> maybe (return True) (processBasicScenario BackgroundKind) mbackground
<*> processFeatureElement scenario
processFeatureElement :: (MonadBaseControl IO m, MonadIO m, Applicative m) =>
FeatureElement -> Execution m Bool
processFeatureElement (FESO _)
= warn "Scenario Outlines are not supported yet." >> return False
processFeatureElement (FES sc) =
processBasicScenario (ScenarioKind $ scTags sc) $ scBasicScenario sc
processBasicScenario :: (MonadBaseControl IO m, MonadIO m, Applicative m) =>
BasicScenarioKind -> BasicScenario -> Execution m Bool
processBasicScenario kind scenario = do
putDoc $ describeBasicScenario kind scenario
processSteps (bsSteps scenario)
processSteps :: (MonadBaseControl IO m, MonadIO m, Applicative m) => Steps -> Execution m Bool
processSteps steps = mapShortCircuitM processStep steps
mapShortCircuitM :: (Monad m) => (a -> m Bool) -> [a] -> m Bool
mapShortCircuitM _ [] = return True
mapShortCircuitM f (x:xs) = do
ret <- f x
if ret then mapShortCircuitM f xs
else return False
processStep :: (MonadBaseControl IO m, MonadIO m, Applicative m) => Step -> Execution m Bool
processStep step = do
parseStep <- ask
case parseStep step of
Left e -> do
let msg = concat [ "The step "
, show (stBody step)
, " doesn't match any step definitions I know."
, show e ]
putDoc $ describeStep (UnknownStep msg) step
return False
Right m -> do
r <- E.catches (lift m >> return SuccessfulStep)
[ E.Handler $ \(e :: E.AsyncException) -> E.throw (e :: E.AsyncException)
, E.Handler $ \(e :: E.SomeException) ->
return (FailedStep $ "Caught exception: " ++ show e) ]
putDoc (describeStep r step)
return (r == SuccessfulStep)
data Options
= Options {file_ :: [FilePath]}
deriving (Eq, Show, Typeable, Data)
getPaths :: IO [FilePath]
getPaths = do
progName <- getProgName
file_ <$> cmdArgs (Options (def &= typ "PATH" &= args)
&= program progName
&= details ["Run one or more abacate files."])