{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ConstraintKinds #-}
module Test.Hspec.Core.Runner.Eval (
  EvalConfig(..)
, EvalTree
, Tree(..)
, EvalItem(..)
, Concurrency(..)
, runFormatter
#ifdef TEST
, mergeResults
#endif
) where

import           Prelude ()
import           Test.Hspec.Core.Compat hiding (Monad)

import           Control.Monad.IO.Class (liftIO)
import           Control.Monad.Trans.Reader
import           Control.Monad.Trans.Class

import           Test.Hspec.Core.Util
import           Test.Hspec.Core.Spec (Progress, FailureReason(..), Result(..), ResultStatus(..), ProgressCallback)
import           Test.Hspec.Core.Timer
import           Test.Hspec.Core.Format (Format)
import qualified Test.Hspec.Core.Format as Format
import           Test.Hspec.Core.Clock
import           Test.Hspec.Core.Example.Location
import           Test.Hspec.Core.Example (safeEvaluateResultStatus, exceptionToResultStatus)

import qualified NonEmpty
import           NonEmpty (NonEmpty(..))

import           Test.Hspec.Core.Runner.JobQueue

data Tree c a =
    Node String (NonEmpty (Tree c a))
  | NodeWithCleanup (Maybe (String, Location)) c (NonEmpty (Tree c a))
  | Leaf a
  deriving (Tree c a -> Tree c a -> Bool
(Tree c a -> Tree c a -> Bool)
-> (Tree c a -> Tree c a -> Bool) -> Eq (Tree c a)
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
forall c a. (Eq c, Eq a) => Tree c a -> Tree c a -> Bool
/= :: Tree c a -> Tree c a -> Bool
$c/= :: forall c a. (Eq c, Eq a) => Tree c a -> Tree c a -> Bool
== :: Tree c a -> Tree c a -> Bool
$c== :: forall c a. (Eq c, Eq a) => Tree c a -> Tree c a -> Bool
Eq, Int -> Tree c a -> ShowS
[Tree c a] -> ShowS
Tree c a -> String
(Int -> Tree c a -> ShowS)
-> (Tree c a -> String) -> ([Tree c a] -> ShowS) -> Show (Tree c a)
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
forall c a. (Show c, Show a) => Int -> Tree c a -> ShowS
forall c a. (Show c, Show a) => [Tree c a] -> ShowS
forall c a. (Show c, Show a) => Tree c a -> String
showList :: [Tree c a] -> ShowS
$cshowList :: forall c a. (Show c, Show a) => [Tree c a] -> ShowS
show :: Tree c a -> String
$cshow :: forall c a. (Show c, Show a) => Tree c a -> String
showsPrec :: Int -> Tree c a -> ShowS
$cshowsPrec :: forall c a. (Show c, Show a) => Int -> Tree c a -> ShowS
Show, a -> Tree c b -> Tree c a
(a -> b) -> Tree c a -> Tree c b
(forall a b. (a -> b) -> Tree c a -> Tree c b)
-> (forall a b. a -> Tree c b -> Tree c a) -> Functor (Tree c)
forall a b. a -> Tree c b -> Tree c a
forall a b. (a -> b) -> Tree c a -> Tree c b
forall c a b. a -> Tree c b -> Tree c a
forall c a b. (a -> b) -> Tree c a -> Tree c b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: a -> Tree c b -> Tree c a
$c<$ :: forall c a b. a -> Tree c b -> Tree c a
fmap :: (a -> b) -> Tree c a -> Tree c b
$cfmap :: forall c a b. (a -> b) -> Tree c a -> Tree c b
Functor, Tree c a -> Bool
(a -> m) -> Tree c a -> m
(a -> b -> b) -> b -> Tree c a -> b
(forall m. Monoid m => Tree c m -> m)
-> (forall m a. Monoid m => (a -> m) -> Tree c a -> m)
-> (forall m a. Monoid m => (a -> m) -> Tree c a -> m)
-> (forall a b. (a -> b -> b) -> b -> Tree c a -> b)
-> (forall a b. (a -> b -> b) -> b -> Tree c a -> b)
-> (forall b a. (b -> a -> b) -> b -> Tree c a -> b)
-> (forall b a. (b -> a -> b) -> b -> Tree c a -> b)
-> (forall a. (a -> a -> a) -> Tree c a -> a)
-> (forall a. (a -> a -> a) -> Tree c a -> a)
-> (forall a. Tree c a -> [a])
-> (forall a. Tree c a -> Bool)
-> (forall a. Tree c a -> Int)
-> (forall a. Eq a => a -> Tree c a -> Bool)
-> (forall a. Ord a => Tree c a -> a)
-> (forall a. Ord a => Tree c a -> a)
-> (forall a. Num a => Tree c a -> a)
-> (forall a. Num a => Tree c a -> a)
-> Foldable (Tree c)
forall a. Eq a => a -> Tree c a -> Bool
forall a. Num a => Tree c a -> a
forall a. Ord a => Tree c a -> a
forall m. Monoid m => Tree c m -> m
forall a. Tree c a -> Bool
forall a. Tree c a -> Int
forall a. Tree c a -> [a]
forall a. (a -> a -> a) -> Tree c a -> a
forall c a. Eq a => a -> Tree c a -> Bool
forall c a. Num a => Tree c a -> a
forall c a. Ord a => Tree c a -> a
forall m a. Monoid m => (a -> m) -> Tree c a -> m
forall c m. Monoid m => Tree c m -> m
forall c a. Tree c a -> Bool
forall c a. Tree c a -> Int
forall c a. Tree c a -> [a]
forall b a. (b -> a -> b) -> b -> Tree c a -> b
forall a b. (a -> b -> b) -> b -> Tree c a -> b
forall c a. (a -> a -> a) -> Tree c a -> a
forall c m a. Monoid m => (a -> m) -> Tree c a -> m
forall c b a. (b -> a -> b) -> b -> Tree c a -> b
forall c a b. (a -> b -> b) -> b -> Tree c a -> b
forall (t :: * -> *).
(forall m. Monoid m => t m -> m)
-> (forall m a. Monoid m => (a -> m) -> t a -> m)
-> (forall m a. Monoid m => (a -> m) -> t a -> m)
-> (forall a b. (a -> b -> b) -> b -> t a -> b)
-> (forall a b. (a -> b -> b) -> b -> t a -> b)
-> (forall b a. (b -> a -> b) -> b -> t a -> b)
-> (forall b a. (b -> a -> b) -> b -> t a -> b)
-> (forall a. (a -> a -> a) -> t a -> a)
-> (forall a. (a -> a -> a) -> t a -> a)
-> (forall a. t a -> [a])
-> (forall a. t a -> Bool)
-> (forall a. t a -> Int)
-> (forall a. Eq a => a -> t a -> Bool)
-> (forall a. Ord a => t a -> a)
-> (forall a. Ord a => t a -> a)
-> (forall a. Num a => t a -> a)
-> (forall a. Num a => t a -> a)
-> Foldable t
product :: Tree c a -> a
$cproduct :: forall c a. Num a => Tree c a -> a
sum :: Tree c a -> a
$csum :: forall c a. Num a => Tree c a -> a
minimum :: Tree c a -> a
$cminimum :: forall c a. Ord a => Tree c a -> a
maximum :: Tree c a -> a
$cmaximum :: forall c a. Ord a => Tree c a -> a
elem :: a -> Tree c a -> Bool
$celem :: forall c a. Eq a => a -> Tree c a -> Bool
length :: Tree c a -> Int
$clength :: forall c a. Tree c a -> Int
null :: Tree c a -> Bool
$cnull :: forall c a. Tree c a -> Bool
toList :: Tree c a -> [a]
$ctoList :: forall c a. Tree c a -> [a]
foldl1 :: (a -> a -> a) -> Tree c a -> a
$cfoldl1 :: forall c a. (a -> a -> a) -> Tree c a -> a
foldr1 :: (a -> a -> a) -> Tree c a -> a
$cfoldr1 :: forall c a. (a -> a -> a) -> Tree c a -> a
foldl' :: (b -> a -> b) -> b -> Tree c a -> b
$cfoldl' :: forall c b a. (b -> a -> b) -> b -> Tree c a -> b
foldl :: (b -> a -> b) -> b -> Tree c a -> b
$cfoldl :: forall c b a. (b -> a -> b) -> b -> Tree c a -> b
foldr' :: (a -> b -> b) -> b -> Tree c a -> b
$cfoldr' :: forall c a b. (a -> b -> b) -> b -> Tree c a -> b
foldr :: (a -> b -> b) -> b -> Tree c a -> b
$cfoldr :: forall c a b. (a -> b -> b) -> b -> Tree c a -> b
foldMap' :: (a -> m) -> Tree c a -> m
$cfoldMap' :: forall c m a. Monoid m => (a -> m) -> Tree c a -> m
foldMap :: (a -> m) -> Tree c a -> m
$cfoldMap :: forall c m a. Monoid m => (a -> m) -> Tree c a -> m
fold :: Tree c m -> m
$cfold :: forall c m. Monoid m => Tree c m -> m
Foldable, Functor (Tree c)
Foldable (Tree c)
Functor (Tree c)
-> Foldable (Tree c)
-> (forall (f :: * -> *) a b.
    Applicative f =>
    (a -> f b) -> Tree c a -> f (Tree c b))
-> (forall (f :: * -> *) a.
    Applicative f =>
    Tree c (f a) -> f (Tree c a))
-> (forall (m :: * -> *) a b.
    Monad m =>
    (a -> m b) -> Tree c a -> m (Tree c b))
-> (forall (m :: * -> *) a.
    Monad m =>
    Tree c (m a) -> m (Tree c a))
-> Traversable (Tree c)
(a -> f b) -> Tree c a -> f (Tree c b)
forall c. Functor (Tree c)
forall c. Foldable (Tree c)
forall c (m :: * -> *) a. Monad m => Tree c (m a) -> m (Tree c a)
forall c (f :: * -> *) a.
Applicative f =>
Tree c (f a) -> f (Tree c a)
forall c (m :: * -> *) a b.
Monad m =>
(a -> m b) -> Tree c a -> m (Tree c b)
forall c (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> Tree c a -> f (Tree c b)
forall (t :: * -> *).
Functor t
-> Foldable t
-> (forall (f :: * -> *) a b.
    Applicative f =>
    (a -> f b) -> t a -> f (t b))
-> (forall (f :: * -> *) a. Applicative f => t (f a) -> f (t a))
-> (forall (m :: * -> *) a b.
    Monad m =>
    (a -> m b) -> t a -> m (t b))
-> (forall (m :: * -> *) a. Monad m => t (m a) -> m (t a))
-> Traversable t
forall (m :: * -> *) a. Monad m => Tree c (m a) -> m (Tree c a)
forall (f :: * -> *) a.
Applicative f =>
Tree c (f a) -> f (Tree c a)
forall (m :: * -> *) a b.
Monad m =>
(a -> m b) -> Tree c a -> m (Tree c b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> Tree c a -> f (Tree c b)
sequence :: Tree c (m a) -> m (Tree c a)
$csequence :: forall c (m :: * -> *) a. Monad m => Tree c (m a) -> m (Tree c a)
mapM :: (a -> m b) -> Tree c a -> m (Tree c b)
$cmapM :: forall c (m :: * -> *) a b.
Monad m =>
(a -> m b) -> Tree c a -> m (Tree c b)
sequenceA :: Tree c (f a) -> f (Tree c a)
$csequenceA :: forall c (f :: * -> *) a.
Applicative f =>
Tree c (f a) -> f (Tree c a)
traverse :: (a -> f b) -> Tree c a -> f (Tree c b)
$ctraverse :: forall c (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> Tree c a -> f (Tree c b)
$cp2Traversable :: forall c. Foldable (Tree c)
$cp1Traversable :: forall c. Functor (Tree c)
Traversable)

data EvalConfig = EvalConfig {
  EvalConfig -> Format
evalConfigFormat :: Format
, EvalConfig -> Int
evalConfigConcurrentJobs :: Int
, EvalConfig -> Bool
evalConfigFailFast :: Bool
}

data Env = Env {
  Env -> EvalConfig
envConfig :: EvalConfig
, Env -> IORef Bool
envFailed :: IORef Bool
, Env -> IORef [(Path, Item)]
envResults :: IORef [(Path, Format.Item)]
}

formatEvent :: Format.Event -> EvalM ()
formatEvent :: Event -> EvalM ()
formatEvent Event
event = do
  Format
format <- (Env -> Format) -> ReaderT Env IO Format
forall (m :: * -> *) r a. Monad m => (r -> a) -> ReaderT r m a
asks ((Env -> Format) -> ReaderT Env IO Format)
-> (Env -> Format) -> ReaderT Env IO Format
forall a b. (a -> b) -> a -> b
$ EvalConfig -> Format
evalConfigFormat (EvalConfig -> Format) -> (Env -> EvalConfig) -> Env -> Format
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Env -> EvalConfig
envConfig
  IO () -> EvalM ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> EvalM ()) -> IO () -> EvalM ()
forall a b. (a -> b) -> a -> b
$ Format
format Event
event

type EvalM = ReaderT Env IO

setFailed :: EvalM ()
setFailed :: EvalM ()
setFailed = do
  IORef Bool
ref <- (Env -> IORef Bool) -> ReaderT Env IO (IORef Bool)
forall (m :: * -> *) r a. Monad m => (r -> a) -> ReaderT r m a
asks Env -> IORef Bool
envFailed
  IO () -> EvalM ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> EvalM ()) -> IO () -> EvalM ()
forall a b. (a -> b) -> a -> b
$ IORef Bool -> Bool -> IO ()
forall a. IORef a -> a -> IO ()
writeIORef IORef Bool
ref Bool
True

hasFailed :: EvalM Bool
hasFailed :: EvalM Bool
hasFailed = do
  IORef Bool
ref <- (Env -> IORef Bool) -> ReaderT Env IO (IORef Bool)
forall (m :: * -> *) r a. Monad m => (r -> a) -> ReaderT r m a
asks Env -> IORef Bool
envFailed
  IO Bool -> EvalM Bool
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Bool -> EvalM Bool) -> IO Bool -> EvalM Bool
forall a b. (a -> b) -> a -> b
$ IORef Bool -> IO Bool
forall a. IORef a -> IO a
readIORef IORef Bool
ref

addResult :: Path -> Format.Item -> EvalM ()
addResult :: Path -> Item -> EvalM ()
addResult Path
path Item
item = do
  IORef [(Path, Item)]
ref <- (Env -> IORef [(Path, Item)])
-> ReaderT Env IO (IORef [(Path, Item)])
forall (m :: * -> *) r a. Monad m => (r -> a) -> ReaderT r m a
asks Env -> IORef [(Path, Item)]
envResults
  IO () -> EvalM ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> EvalM ()) -> IO () -> EvalM ()
forall a b. (a -> b) -> a -> b
$ IORef [(Path, Item)] -> ([(Path, Item)] -> [(Path, Item)]) -> IO ()
forall a. IORef a -> (a -> a) -> IO ()
modifyIORef IORef [(Path, Item)]
ref ((Path
path, Item
item) (Path, Item) -> [(Path, Item)] -> [(Path, Item)]
forall a. a -> [a] -> [a]
:)

reportItem :: Path -> Maybe Location -> EvalM (Seconds, Result)  -> EvalM ()
reportItem :: Path -> Maybe Location -> EvalM (Seconds, Result) -> EvalM ()
reportItem Path
path Maybe Location
loc EvalM (Seconds, Result)
action = do
  Path -> EvalM ()
reportItemStarted Path
path
  EvalM (Seconds, Result)
action EvalM (Seconds, Result)
-> ((Seconds, Result) -> EvalM ()) -> EvalM ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Path -> Maybe Location -> (Seconds, Result) -> EvalM ()
reportResult Path
path Maybe Location
loc

reportItemStarted :: Path -> EvalM ()
reportItemStarted :: Path -> EvalM ()
reportItemStarted = Event -> EvalM ()
formatEvent (Event -> EvalM ()) -> (Path -> Event) -> Path -> EvalM ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Path -> Event
Format.ItemStarted

reportItemDone :: Path -> Format.Item -> EvalM ()
reportItemDone :: Path -> Item -> EvalM ()
reportItemDone Path
path Item
item = do
  let
    isFailure :: Bool
isFailure = case Item -> Result
Format.itemResult Item
item of
      Format.Success{} -> Bool
False
      Format.Pending{} -> Bool
False
      Format.Failure{} -> Bool
True
  Bool -> EvalM () -> EvalM ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when Bool
isFailure EvalM ()
setFailed
  Path -> Item -> EvalM ()
addResult Path
path Item
item
  Event -> EvalM ()
formatEvent (Event -> EvalM ()) -> Event -> EvalM ()
forall a b. (a -> b) -> a -> b
$ Path -> Item -> Event
Format.ItemDone Path
path Item
item

reportResult :: Path -> Maybe Location -> (Seconds, Result) -> EvalM ()
reportResult :: Path -> Maybe Location -> (Seconds, Result) -> EvalM ()
reportResult Path
path Maybe Location
loc (Seconds
duration, Result
result) = do
  case Result
result of
    Result String
info ResultStatus
status -> Path -> Item -> EvalM ()
reportItemDone Path
path (Item -> EvalM ()) -> Item -> EvalM ()
forall a b. (a -> b) -> a -> b
$ Maybe Location -> Seconds -> String -> Result -> Item
Format.Item Maybe Location
loc Seconds
duration String
info (Result -> Item) -> Result -> Item
forall a b. (a -> b) -> a -> b
$ case ResultStatus
status of
      ResultStatus
Success                      -> Result
Format.Success
      Pending Maybe Location
loc_ Maybe String
reason          -> Maybe Location -> Maybe String -> Result
Format.Pending Maybe Location
loc_ Maybe String
reason
      Failure Maybe Location
loc_ err :: FailureReason
err@(Error Maybe String
_ SomeException
e) -> Maybe Location -> FailureReason -> Result
Format.Failure (Maybe Location
loc_ Maybe Location -> Maybe Location -> Maybe Location
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> SomeException -> Maybe Location
extractLocation SomeException
e) FailureReason
err
      Failure Maybe Location
loc_ FailureReason
err             -> Maybe Location -> FailureReason -> Result
Format.Failure Maybe Location
loc_ FailureReason
err

groupStarted :: Path -> EvalM ()
groupStarted :: Path -> EvalM ()
groupStarted = Event -> EvalM ()
formatEvent (Event -> EvalM ()) -> (Path -> Event) -> Path -> EvalM ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Path -> Event
Format.GroupStarted

groupDone :: Path -> EvalM ()
groupDone :: Path -> EvalM ()
groupDone = Event -> EvalM ()
formatEvent (Event -> EvalM ()) -> (Path -> Event) -> Path -> EvalM ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Path -> Event
Format.GroupDone

data EvalItem = EvalItem {
  EvalItem -> String
evalItemDescription :: String
, EvalItem -> Maybe Location
evalItemLocation :: Maybe Location
, EvalItem -> Concurrency
evalItemConcurrency :: Concurrency
, EvalItem -> ProgressCallback -> IO (Seconds, Result)
evalItemAction :: ProgressCallback -> IO (Seconds, Result)
}

type EvalTree = Tree (IO ()) EvalItem

-- | Evaluate all examples of a given spec and produce a report.
runFormatter :: EvalConfig -> [EvalTree] -> IO ([(Path, Format.Item)])
runFormatter :: EvalConfig -> [EvalTree] -> IO [(Path, Item)]
runFormatter EvalConfig
config [EvalTree]
specs = do
  Int -> (JobQueue -> IO [(Path, Item)]) -> IO [(Path, Item)]
forall a. Int -> (JobQueue -> IO a) -> IO a
withJobQueue (EvalConfig -> Int
evalConfigConcurrentJobs EvalConfig
config) ((JobQueue -> IO [(Path, Item)]) -> IO [(Path, Item)])
-> (JobQueue -> IO [(Path, Item)]) -> IO [(Path, Item)]
forall a b. (a -> b) -> a -> b
$ \ JobQueue
queue -> do
    Seconds -> (IO Bool -> IO [(Path, Item)]) -> IO [(Path, Item)]
forall a. Seconds -> (IO Bool -> IO a) -> IO a
withTimer Seconds
0.05 ((IO Bool -> IO [(Path, Item)]) -> IO [(Path, Item)])
-> (IO Bool -> IO [(Path, Item)]) -> IO [(Path, Item)]
forall a b. (a -> b) -> a -> b
$ \ IO Bool
timer -> do
      Env
env <- IO Env
mkEnv
      [RunningTree_ IO]
runningSpecs_ <- JobQueue -> [EvalTree] -> IO [RunningTree_ IO]
forall (m :: * -> *).
MonadIO m =>
JobQueue -> [EvalTree] -> IO [RunningTree_ m]
enqueueItems JobQueue
queue [EvalTree]
specs

      let
        applyReportProgress :: RunningItem_ IO -> RunningItem
        applyReportProgress :: RunningItem_ IO -> RunningItem
applyReportProgress RunningItem_ IO
item = ((ProgressCallback -> IO (Seconds, Result))
 -> Path -> IO (Seconds, Result))
-> RunningItem_ IO -> RunningItem
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((ProgressCallback -> IO (Seconds, Result))
-> (Path -> ProgressCallback) -> Path -> IO (Seconds, Result)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IO Bool -> Path -> ProgressCallback
reportProgress IO Bool
timer) RunningItem_ IO
item

        runningSpecs :: [RunningTree ()]
        runningSpecs :: [RunningTree ()]
runningSpecs = [RunningTree (IO ())] -> [RunningTree ()]
applyCleanup ([RunningTree (IO ())] -> [RunningTree ()])
-> [RunningTree (IO ())] -> [RunningTree ()]
forall a b. (a -> b) -> a -> b
$ (RunningTree_ IO -> RunningTree (IO ()))
-> [RunningTree_ IO] -> [RunningTree (IO ())]
forall a b. (a -> b) -> [a] -> [b]
map ((RunningItem_ IO -> RunningItem)
-> RunningTree_ IO -> RunningTree (IO ())
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap RunningItem_ IO -> RunningItem
applyReportProgress) [RunningTree_ IO]
runningSpecs_

        getResults :: IO [(Path, Format.Item)]
        getResults :: IO [(Path, Item)]
getResults = [(Path, Item)] -> [(Path, Item)]
forall a. [a] -> [a]
reverse ([(Path, Item)] -> [(Path, Item)])
-> IO [(Path, Item)] -> IO [(Path, Item)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IORef [(Path, Item)] -> IO [(Path, Item)]
forall a. IORef a -> IO a
readIORef (Env -> IORef [(Path, Item)]
envResults Env
env)

        formatItems :: IO ()
        formatItems :: IO ()
formatItems = EvalM () -> Env -> IO ()
forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT ([RunningTree ()] -> EvalM ()
eval [RunningTree ()]
runningSpecs) Env
env

        formatDone :: IO ()
        formatDone :: IO ()
formatDone = IO [(Path, Item)]
getResults IO [(Path, Item)] -> ([(Path, Item)] -> IO ()) -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Format
format Format -> ([(Path, Item)] -> Event) -> [(Path, Item)] -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(Path, Item)] -> Event
Format.Done

      Format
format Event
Format.Started
      IO ()
formatItems IO () -> IO () -> IO ()
forall a b. IO a -> IO b -> IO a
`finally` IO ()
formatDone
      IO [(Path, Item)]
getResults
  where
    mkEnv :: IO Env
    mkEnv :: IO Env
mkEnv = EvalConfig -> IORef Bool -> IORef [(Path, Item)] -> Env
Env EvalConfig
config (IORef Bool -> IORef [(Path, Item)] -> Env)
-> IO (IORef Bool) -> IO (IORef [(Path, Item)] -> Env)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Bool -> IO (IORef Bool)
forall a. a -> IO (IORef a)
newIORef Bool
False IO (IORef [(Path, Item)] -> Env)
-> IO (IORef [(Path, Item)]) -> IO Env
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> [(Path, Item)] -> IO (IORef [(Path, Item)])
forall a. a -> IO (IORef a)
newIORef []

    format :: Format
    format :: Format
format = EvalConfig -> Format
evalConfigFormat EvalConfig
config

    reportProgress :: IO Bool -> Path -> Progress -> IO ()
    reportProgress :: IO Bool -> Path -> ProgressCallback
reportProgress IO Bool
timer Path
path Progress
progress = do
      Bool
r <- IO Bool
timer
      Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when Bool
r (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
        Format
format (Path -> Progress -> Event
Format.Progress Path
path Progress
progress)

data Item a = Item {
  Item a -> String
itemDescription :: String
, Item a -> Maybe Location
itemLocation :: Maybe Location
, Item a -> a
itemAction :: a
} deriving a -> Item b -> Item a
(a -> b) -> Item a -> Item b
(forall a b. (a -> b) -> Item a -> Item b)
-> (forall a b. a -> Item b -> Item a) -> Functor Item
forall a b. a -> Item b -> Item a
forall a b. (a -> b) -> Item a -> Item b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: a -> Item b -> Item a
$c<$ :: forall a b. a -> Item b -> Item a
fmap :: (a -> b) -> Item a -> Item b
$cfmap :: forall a b. (a -> b) -> Item a -> Item b
Functor

type RunningItem = Item (Path -> IO (Seconds, Result))
type RunningTree c = Tree c RunningItem

type RunningItem_ m = Item (Job m Progress (Seconds, Result))
type RunningTree_ m = Tree (IO ()) (RunningItem_ m)

applyCleanup :: [RunningTree (IO ())] -> [RunningTree ()]
applyCleanup :: [RunningTree (IO ())] -> [RunningTree ()]
applyCleanup = (RunningTree (IO ()) -> RunningTree ())
-> [RunningTree (IO ())] -> [RunningTree ()]
forall a b. (a -> b) -> [a] -> [b]
map RunningTree (IO ()) -> RunningTree ()
go
  where
    go :: RunningTree (IO ()) -> RunningTree ()
go RunningTree (IO ())
t = case RunningTree (IO ())
t of
      Node String
label NonEmpty (RunningTree (IO ()))
xs -> String -> NonEmpty (RunningTree ()) -> RunningTree ()
forall c a. String -> NonEmpty (Tree c a) -> Tree c a
Node String
label (RunningTree (IO ()) -> RunningTree ()
go (RunningTree (IO ()) -> RunningTree ())
-> NonEmpty (RunningTree (IO ())) -> NonEmpty (RunningTree ())
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> NonEmpty (RunningTree (IO ()))
xs)
      NodeWithCleanup Maybe (String, Location)
loc IO ()
cleanup NonEmpty (RunningTree (IO ()))
xs -> Maybe (String, Location)
-> () -> NonEmpty (RunningTree ()) -> RunningTree ()
forall c a.
Maybe (String, Location) -> c -> NonEmpty (Tree c a) -> Tree c a
NodeWithCleanup Maybe (String, Location)
loc () (Maybe (String, Location)
-> IO () -> NonEmpty (RunningTree ()) -> NonEmpty (RunningTree ())
addCleanupToLastLeaf Maybe (String, Location)
loc IO ()
cleanup (NonEmpty (RunningTree ()) -> NonEmpty (RunningTree ()))
-> NonEmpty (RunningTree ()) -> NonEmpty (RunningTree ())
forall a b. (a -> b) -> a -> b
$ RunningTree (IO ()) -> RunningTree ()
go (RunningTree (IO ()) -> RunningTree ())
-> NonEmpty (RunningTree (IO ())) -> NonEmpty (RunningTree ())
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> NonEmpty (RunningTree (IO ()))
xs)
      Leaf RunningItem
a -> RunningItem -> RunningTree ()
forall c a. a -> Tree c a
Leaf RunningItem
a

addCleanupToLastLeaf :: Maybe (String, Location) -> IO () -> NonEmpty (RunningTree ()) -> NonEmpty (RunningTree ())
addCleanupToLastLeaf :: Maybe (String, Location)
-> IO () -> NonEmpty (RunningTree ()) -> NonEmpty (RunningTree ())
addCleanupToLastLeaf Maybe (String, Location)
loc IO ()
cleanup = NonEmpty (RunningTree ()) -> NonEmpty (RunningTree ())
go
  where
    go :: NonEmpty (RunningTree ()) -> NonEmpty (RunningTree ())
go = NonEmpty (RunningTree ()) -> NonEmpty (RunningTree ())
forall a. NonEmpty a -> NonEmpty a
NonEmpty.reverse (NonEmpty (RunningTree ()) -> NonEmpty (RunningTree ()))
-> (NonEmpty (RunningTree ()) -> NonEmpty (RunningTree ()))
-> NonEmpty (RunningTree ())
-> NonEmpty (RunningTree ())
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (RunningTree () -> RunningTree ())
-> NonEmpty (RunningTree ()) -> NonEmpty (RunningTree ())
forall a. (a -> a) -> NonEmpty a -> NonEmpty a
mapHead RunningTree () -> RunningTree ()
goNode (NonEmpty (RunningTree ()) -> NonEmpty (RunningTree ()))
-> (NonEmpty (RunningTree ()) -> NonEmpty (RunningTree ()))
-> NonEmpty (RunningTree ())
-> NonEmpty (RunningTree ())
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NonEmpty (RunningTree ()) -> NonEmpty (RunningTree ())
forall a. NonEmpty a -> NonEmpty a
NonEmpty.reverse

    goNode :: RunningTree () -> RunningTree ()
goNode RunningTree ()
node = case RunningTree ()
node of
      Node String
description NonEmpty (RunningTree ())
xs -> String -> NonEmpty (RunningTree ()) -> RunningTree ()
forall c a. String -> NonEmpty (Tree c a) -> Tree c a
Node String
description (NonEmpty (RunningTree ()) -> NonEmpty (RunningTree ())
go NonEmpty (RunningTree ())
xs)
      NodeWithCleanup Maybe (String, Location)
loc_ () NonEmpty (RunningTree ())
xs -> Maybe (String, Location)
-> () -> NonEmpty (RunningTree ()) -> RunningTree ()
forall c a.
Maybe (String, Location) -> c -> NonEmpty (Tree c a) -> Tree c a
NodeWithCleanup Maybe (String, Location)
loc_ () (NonEmpty (RunningTree ()) -> NonEmpty (RunningTree ())
go NonEmpty (RunningTree ())
xs)
      Leaf RunningItem
item -> RunningItem -> RunningTree ()
forall c a. a -> Tree c a
Leaf (Maybe (String, Location) -> IO () -> RunningItem -> RunningItem
addCleanupToItem Maybe (String, Location)
loc IO ()
cleanup RunningItem
item)

mapHead :: (a -> a) -> NonEmpty a -> NonEmpty a
mapHead :: (a -> a) -> NonEmpty a -> NonEmpty a
mapHead a -> a
f NonEmpty a
xs = case NonEmpty a
xs of
  a
y :| [a]
ys -> a -> a
f a
y a -> [a] -> NonEmpty a
forall a. a -> [a] -> NonEmpty a
:| [a]
ys

addCleanupToItem :: Maybe (String, Location) -> IO () -> RunningItem -> RunningItem
addCleanupToItem :: Maybe (String, Location) -> IO () -> RunningItem -> RunningItem
addCleanupToItem Maybe (String, Location)
loc IO ()
cleanup RunningItem
item = RunningItem
item {
  itemAction :: Path -> IO (Seconds, Result)
itemAction = \ Path
path -> do
    (Seconds
t1, Result
r1) <- RunningItem -> Path -> IO (Seconds, Result)
forall a. Item a -> a
itemAction RunningItem
item Path
path
    (Seconds
t2, ResultStatus
r2) <- IO ResultStatus -> IO (Seconds, ResultStatus)
forall a. IO a -> IO (Seconds, a)
measure (IO ResultStatus -> IO (Seconds, ResultStatus))
-> IO ResultStatus -> IO (Seconds, ResultStatus)
forall a b. (a -> b) -> a -> b
$ IO ResultStatus -> IO ResultStatus
safeEvaluateResultStatus (IO ()
cleanup IO () -> IO ResultStatus -> IO ResultStatus
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> ResultStatus -> IO ResultStatus
forall (m :: * -> *) a. Monad m => a -> m a
return ResultStatus
Success)
    let t :: Seconds
t = Seconds
t1 Seconds -> Seconds -> Seconds
forall a. Num a => a -> a -> a
+ Seconds
t2
    (Seconds, Result) -> IO (Seconds, Result)
forall (m :: * -> *) a. Monad m => a -> m a
return (Seconds
t, Maybe (String, Location) -> Result -> ResultStatus -> Result
mergeResults Maybe (String, Location)
loc Result
r1 ResultStatus
r2)
}

mergeResults :: Maybe (String, Location) -> Result -> ResultStatus -> Result
mergeResults :: Maybe (String, Location) -> Result -> ResultStatus -> Result
mergeResults Maybe (String, Location)
mCallSite (Result String
info ResultStatus
r1) ResultStatus
r2 = String -> ResultStatus -> Result
Result String
info (ResultStatus -> Result) -> ResultStatus -> Result
forall a b. (a -> b) -> a -> b
$ case (ResultStatus
r1, ResultStatus
r2) of
  (ResultStatus
_, ResultStatus
Success) -> ResultStatus
r1
  (Failure{}, ResultStatus
_) -> ResultStatus
r1
  (Pending{}, Pending{}) -> ResultStatus
r1
  (ResultStatus
Success, Pending{}) -> ResultStatus
r2
  (ResultStatus
_, Failure Maybe Location
mLoc FailureReason
err) -> Maybe Location -> FailureReason -> ResultStatus
Failure (Maybe Location
mLoc Maybe Location -> Maybe Location -> Maybe Location
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Maybe Location
hookLoc) (FailureReason -> ResultStatus) -> FailureReason -> ResultStatus
forall a b. (a -> b) -> a -> b
$ case FailureReason
err of
    Error Maybe String
message SomeException
e -> Maybe String -> SomeException -> FailureReason
Error (Maybe String
message Maybe String -> Maybe String -> Maybe String
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Maybe String
hookFailed) SomeException
e
    FailureReason
_ -> FailureReason
err
  where
    hookLoc :: Maybe Location
hookLoc = (String, Location) -> Location
forall a b. (a, b) -> b
snd ((String, Location) -> Location)
-> Maybe (String, Location) -> Maybe Location
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe (String, Location)
mCallSite
    hookFailed :: Maybe String
hookFailed = case Maybe (String, Location)
mCallSite of
      Just (String
name, Location
_) -> String -> Maybe String
forall a. a -> Maybe a
Just (String -> Maybe String) -> String -> Maybe String
forall a b. (a -> b) -> a -> b
$ String
"in " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
name String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"-hook:"
      Maybe (String, Location)
Nothing -> Maybe String
forall a. Maybe a
Nothing

enqueueItems :: MonadIO m => JobQueue -> [EvalTree] -> IO [RunningTree_ m]
enqueueItems :: JobQueue -> [EvalTree] -> IO [RunningTree_ m]
enqueueItems JobQueue
queue = (EvalTree -> IO (RunningTree_ m))
-> [EvalTree] -> IO [RunningTree_ m]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM ((EvalItem -> IO (RunningItem_ m))
-> EvalTree -> IO (RunningTree_ m)
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
traverse ((EvalItem -> IO (RunningItem_ m))
 -> EvalTree -> IO (RunningTree_ m))
-> (EvalItem -> IO (RunningItem_ m))
-> EvalTree
-> IO (RunningTree_ m)
forall a b. (a -> b) -> a -> b
$ JobQueue -> EvalItem -> IO (RunningItem_ m)
forall (m :: * -> *).
MonadIO m =>
JobQueue -> EvalItem -> IO (RunningItem_ m)
enqueueItem JobQueue
queue)

enqueueItem :: MonadIO m => JobQueue -> EvalItem -> IO (RunningItem_ m)
enqueueItem :: JobQueue -> EvalItem -> IO (RunningItem_ m)
enqueueItem JobQueue
queue EvalItem{String
Maybe Location
Concurrency
ProgressCallback -> IO (Seconds, Result)
evalItemAction :: ProgressCallback -> IO (Seconds, Result)
evalItemConcurrency :: Concurrency
evalItemLocation :: Maybe Location
evalItemDescription :: String
evalItemAction :: EvalItem -> ProgressCallback -> IO (Seconds, Result)
evalItemConcurrency :: EvalItem -> Concurrency
evalItemLocation :: EvalItem -> Maybe Location
evalItemDescription :: EvalItem -> String
..} = do
  Job m Progress (Either SomeException (Seconds, Result))
job <- JobQueue
-> Concurrency
-> (ProgressCallback -> IO (Seconds, Result))
-> IO (Job m Progress (Either SomeException (Seconds, Result)))
forall (m :: * -> *) progress a.
MonadIO m =>
JobQueue
-> Concurrency
-> Job IO progress a
-> IO (Job m progress (Either SomeException a))
enqueueJob JobQueue
queue Concurrency
evalItemConcurrency ProgressCallback -> IO (Seconds, Result)
evalItemAction
  RunningItem_ m -> IO (RunningItem_ m)
forall (m :: * -> *) a. Monad m => a -> m a
return Item :: forall a. String -> Maybe Location -> a -> Item a
Item {
    itemDescription :: String
itemDescription = String
evalItemDescription
  , itemLocation :: Maybe Location
itemLocation = Maybe Location
evalItemLocation
  , itemAction :: (Progress -> m ()) -> m (Seconds, Result)
itemAction = Job m Progress (Either SomeException (Seconds, Result))
job Job m Progress (Either SomeException (Seconds, Result))
-> (Either SomeException (Seconds, Result) -> m (Seconds, Result))
-> (Progress -> m ())
-> m (Seconds, Result)
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> IO (Seconds, Result) -> m (Seconds, Result)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Seconds, Result) -> m (Seconds, Result))
-> (Either SomeException (Seconds, Result) -> IO (Seconds, Result))
-> Either SomeException (Seconds, Result)
-> m (Seconds, Result)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (SomeException -> IO (Seconds, Result))
-> ((Seconds, Result) -> IO (Seconds, Result))
-> Either SomeException (Seconds, Result)
-> IO (Seconds, Result)
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either SomeException -> IO (Seconds, Result)
exceptionToResult (Seconds, Result) -> IO (Seconds, Result)
forall (m :: * -> *) a. Monad m => a -> m a
return
  }
  where
    exceptionToResult :: SomeException -> IO (Seconds, Result)
    exceptionToResult :: SomeException -> IO (Seconds, Result)
exceptionToResult SomeException
err = (,) Seconds
0 (Result -> (Seconds, Result))
-> (ResultStatus -> Result) -> ResultStatus -> (Seconds, Result)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> ResultStatus -> Result
Result String
"" (ResultStatus -> (Seconds, Result))
-> IO ResultStatus -> IO (Seconds, Result)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SomeException -> IO ResultStatus
exceptionToResultStatus SomeException
err

eval :: [RunningTree ()] -> EvalM ()
eval :: [RunningTree ()] -> EvalM ()
eval [RunningTree ()]
specs = do
  Bool
failFast <- (Env -> Bool) -> EvalM Bool
forall (m :: * -> *) r a. Monad m => (r -> a) -> ReaderT r m a
asks (EvalConfig -> Bool
evalConfigFailFast (EvalConfig -> Bool) -> (Env -> EvalConfig) -> Env -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Env -> EvalConfig
envConfig)
  Bool -> [EvalM ()] -> EvalM ()
sequenceActions Bool
failFast ((RunningTree () -> [EvalM ()]) -> [RunningTree ()] -> [EvalM ()]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap RunningTree () -> [EvalM ()]
foldSpec [RunningTree ()]
specs)
  where
    foldSpec :: RunningTree () -> [EvalM ()]
    foldSpec :: RunningTree () -> [EvalM ()]
foldSpec = FoldTree () RunningItem (EvalM ()) -> RunningTree () -> [EvalM ()]
forall c a r. FoldTree c a r -> Tree c a -> [r]
foldTree FoldTree :: forall c a r.
(Path -> r)
-> (Path -> r)
-> (Maybe (String, Location) -> [String] -> c -> r)
-> ([String] -> a -> r)
-> FoldTree c a r
FoldTree {
      onGroupStarted :: Path -> EvalM ()
onGroupStarted = Path -> EvalM ()
groupStarted
    , onGroupDone :: Path -> EvalM ()
onGroupDone = Path -> EvalM ()
groupDone
    , onCleanup :: Maybe (String, Location) -> [String] -> () -> EvalM ()
onCleanup = Maybe (String, Location) -> [String] -> () -> EvalM ()
runCleanup
    , onLeafe :: [String] -> RunningItem -> EvalM ()
onLeafe = [String] -> RunningItem -> EvalM ()
evalItem
    }

    runCleanup :: Maybe (String, Location) -> [String] -> () -> EvalM ()
    runCleanup :: Maybe (String, Location) -> [String] -> () -> EvalM ()
runCleanup Maybe (String, Location)
_loc [String]
_groups = () -> EvalM ()
forall (m :: * -> *) a. Monad m => a -> m a
return

    evalItem :: [String] -> RunningItem -> EvalM ()
    evalItem :: [String] -> RunningItem -> EvalM ()
evalItem [String]
groups (Item String
requirement Maybe Location
loc Path -> IO (Seconds, Result)
action) = do
      Path -> Maybe Location -> EvalM (Seconds, Result) -> EvalM ()
reportItem Path
path Maybe Location
loc (EvalM (Seconds, Result) -> EvalM ())
-> EvalM (Seconds, Result) -> EvalM ()
forall a b. (a -> b) -> a -> b
$ IO (Seconds, Result) -> EvalM (Seconds, Result)
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (Path -> IO (Seconds, Result)
action Path
path)
      where
        path :: Path
        path :: Path
path = ([String]
groups, String
requirement)

data FoldTree c a r = FoldTree {
  FoldTree c a r -> Path -> r
onGroupStarted :: Path -> r
, FoldTree c a r -> Path -> r
onGroupDone :: Path -> r
, FoldTree c a r -> Maybe (String, Location) -> [String] -> c -> r
onCleanup :: Maybe (String, Location) -> [String] -> c -> r
, FoldTree c a r -> [String] -> a -> r
onLeafe :: [String] -> a -> r
}

foldTree :: FoldTree c a r -> Tree c a -> [r]
foldTree :: FoldTree c a r -> Tree c a -> [r]
foldTree FoldTree{[String] -> a -> r
Maybe (String, Location) -> [String] -> c -> r
Path -> r
onLeafe :: [String] -> a -> r
onCleanup :: Maybe (String, Location) -> [String] -> c -> r
onGroupDone :: Path -> r
onGroupStarted :: Path -> r
onLeafe :: forall c a r. FoldTree c a r -> [String] -> a -> r
onCleanup :: forall c a r.
FoldTree c a r -> Maybe (String, Location) -> [String] -> c -> r
onGroupDone :: forall c a r. FoldTree c a r -> Path -> r
onGroupStarted :: forall c a r. FoldTree c a r -> Path -> r
..} = [String] -> Tree c a -> [r]
go []
  where
    go :: [String] -> Tree c a -> [r]
go [String]
rGroups (Node String
group NonEmpty (Tree c a)
xs) = r
start r -> [r] -> [r]
forall a. a -> [a] -> [a]
: [r]
children [r] -> [r] -> [r]
forall a. [a] -> [a] -> [a]
++ [r
done]
      where
        path :: Path
path = ([String] -> [String]
forall a. [a] -> [a]
reverse [String]
rGroups, String
group)
        start :: r
start = Path -> r
onGroupStarted Path
path
        children :: [r]
children = (Tree c a -> [r]) -> NonEmpty (Tree c a) -> [r]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap ([String] -> Tree c a -> [r]
go (String
group String -> [String] -> [String]
forall a. a -> [a] -> [a]
: [String]
rGroups)) NonEmpty (Tree c a)
xs
        done :: r
done =  Path -> r
onGroupDone Path
path
    go [String]
rGroups (NodeWithCleanup Maybe (String, Location)
loc c
action NonEmpty (Tree c a)
xs) = [r]
children [r] -> [r] -> [r]
forall a. [a] -> [a] -> [a]
++ [r
cleanup]
      where
        children :: [r]
children = (Tree c a -> [r]) -> NonEmpty (Tree c a) -> [r]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap ([String] -> Tree c a -> [r]
go [String]
rGroups) NonEmpty (Tree c a)
xs
        cleanup :: r
cleanup = Maybe (String, Location) -> [String] -> c -> r
onCleanup Maybe (String, Location)
loc ([String] -> [String]
forall a. [a] -> [a]
reverse [String]
rGroups) c
action
    go [String]
rGroups (Leaf a
a) = [[String] -> a -> r
onLeafe ([String] -> [String]
forall a. [a] -> [a]
reverse [String]
rGroups) a
a]

sequenceActions :: Bool -> [EvalM ()] -> EvalM ()
sequenceActions :: Bool -> [EvalM ()] -> EvalM ()
sequenceActions Bool
failFast = [EvalM ()] -> EvalM ()
go
  where
    go :: [EvalM ()] -> EvalM ()
    go :: [EvalM ()] -> EvalM ()
go [] = EvalM ()
forall (m :: * -> *). Applicative m => m ()
pass
    go (EvalM ()
action : [EvalM ()]
actions) = do
      EvalM ()
action
      Bool
stopNow <- case Bool
failFast of
        Bool
False -> Bool -> EvalM Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
        Bool
True -> EvalM Bool
hasFailed
      Bool -> EvalM () -> EvalM ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless Bool
stopNow ([EvalM ()] -> EvalM ()
go [EvalM ()]
actions)