-- | Stability: provisional
module Test.Hspec.Core.Hooks (
  before
, before_
, beforeWith
, beforeAll
, beforeAll_
, after
, after_
, afterAll
, afterAll_
, around
, around_
, aroundWith
) where

import           Control.Exception (SomeException, finally, throwIO, try)
import           Control.Concurrent.MVar

import           Test.Hspec.Core.Example
import           Test.Hspec.Core.Tree
import           Test.Hspec.Core.Spec.Monad

-- | Run a custom action before every spec item.
before :: IO a -> SpecWith a -> Spec
before :: IO a -> SpecWith a -> Spec
before IO a
action = (ActionWith a -> IO ()) -> SpecWith a -> Spec
forall a. (ActionWith a -> IO ()) -> SpecWith a -> Spec
around (IO a
action IO a -> ActionWith a -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>=)

-- | Run a custom action before every spec item.
before_ :: IO () -> SpecWith a -> SpecWith a
before_ :: IO () -> SpecWith a -> SpecWith a
before_ IO ()
action = (IO () -> IO ()) -> SpecWith a -> SpecWith a
forall a. (IO () -> IO ()) -> SpecWith a -> SpecWith a
around_ (IO ()
action IO () -> IO () -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>>)

-- | Run a custom action before every spec item.
beforeWith :: (b -> IO a) -> SpecWith a -> SpecWith b
beforeWith :: (b -> IO a) -> SpecWith a -> SpecWith b
beforeWith b -> IO a
action = (ActionWith a -> ActionWith b) -> SpecWith a -> SpecWith b
forall a b.
(ActionWith a -> ActionWith b) -> SpecWith a -> SpecWith b
aroundWith ((ActionWith a -> ActionWith b) -> SpecWith a -> SpecWith b)
-> (ActionWith a -> ActionWith b) -> SpecWith a -> SpecWith b
forall a b. (a -> b) -> a -> b
$ \ActionWith a
e b
x -> b -> IO a
action b
x IO a -> ActionWith a -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= ActionWith a
e

-- | Run a custom action before the first spec item.
beforeAll :: IO a -> SpecWith a -> Spec
beforeAll :: IO a -> SpecWith a -> Spec
beforeAll IO a
action SpecWith a
spec = do
  MVar (Memoized a)
mvar <- IO (MVar (Memoized a)) -> SpecM () (MVar (Memoized a))
forall r a. IO r -> SpecM a r
runIO (Memoized a -> IO (MVar (Memoized a))
forall a. a -> IO (MVar a)
newMVar Memoized a
forall a. Memoized a
Empty)
  IO a -> SpecWith a -> Spec
forall a. IO a -> SpecWith a -> Spec
before (MVar (Memoized a) -> IO a -> IO a
forall a. MVar (Memoized a) -> IO a -> IO a
memoize MVar (Memoized a)
mvar IO a
action) SpecWith a
spec

-- | Run a custom action before the first spec item.
beforeAll_ :: IO () -> SpecWith a -> SpecWith a
beforeAll_ :: IO () -> SpecWith a -> SpecWith a
beforeAll_ IO ()
action SpecWith a
spec = do
  MVar (Memoized ())
mvar <- IO (MVar (Memoized ())) -> SpecM a (MVar (Memoized ()))
forall r a. IO r -> SpecM a r
runIO (Memoized () -> IO (MVar (Memoized ()))
forall a. a -> IO (MVar a)
newMVar Memoized ()
forall a. Memoized a
Empty)
  IO () -> SpecWith a -> SpecWith a
forall a. IO () -> SpecWith a -> SpecWith a
before_ (MVar (Memoized ()) -> IO () -> IO ()
forall a. MVar (Memoized a) -> IO a -> IO a
memoize MVar (Memoized ())
mvar IO ()
action) SpecWith a
spec

data Memoized a =
    Empty
  | Memoized a
  | Failed SomeException

memoize :: MVar (Memoized a) -> IO a -> IO a
memoize :: MVar (Memoized a) -> IO a -> IO a
memoize MVar (Memoized a)
mvar IO a
action = do
  Either SomeException a
result <- MVar (Memoized a)
-> (Memoized a -> IO (Memoized a, Either SomeException a))
-> IO (Either SomeException a)
forall a b. MVar a -> (a -> IO (a, b)) -> IO b
modifyMVar MVar (Memoized a)
mvar ((Memoized a -> IO (Memoized a, Either SomeException a))
 -> IO (Either SomeException a))
-> (Memoized a -> IO (Memoized a, Either SomeException a))
-> IO (Either SomeException a)
forall a b. (a -> b) -> a -> b
$ \Memoized a
ma -> case Memoized a
ma of
    Memoized a
Empty -> do
      Either SomeException a
a <- IO a -> IO (Either SomeException a)
forall e a. Exception e => IO a -> IO (Either e a)
try IO a
action
      (Memoized a, Either SomeException a)
-> IO (Memoized a, Either SomeException a)
forall (m :: * -> *) a. Monad m => a -> m a
return ((SomeException -> Memoized a)
-> (a -> Memoized a) -> Either SomeException a -> Memoized a
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either SomeException -> Memoized a
forall a. SomeException -> Memoized a
Failed a -> Memoized a
forall a. a -> Memoized a
Memoized Either SomeException a
a, Either SomeException a
a)
    Memoized a
a -> (Memoized a, Either SomeException a)
-> IO (Memoized a, Either SomeException a)
forall (m :: * -> *) a. Monad m => a -> m a
return (Memoized a
ma, a -> Either SomeException a
forall a b. b -> Either a b
Right a
a)
    Failed SomeException
_ -> ResultStatus -> IO (Memoized a, Either SomeException a)
forall e a. Exception e => e -> IO a
throwIO (Maybe Location -> Maybe String -> ResultStatus
Pending Maybe Location
forall a. Maybe a
Nothing (String -> Maybe String
forall a. a -> Maybe a
Just String
"exception in beforeAll-hook (see previous failure)"))
  (SomeException -> IO a)
-> (a -> IO a) -> Either SomeException a -> IO a
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either SomeException -> IO a
forall e a. Exception e => e -> IO a
throwIO a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Either SomeException a
result

-- | Run a custom action after every spec item.
after :: ActionWith a -> SpecWith a -> SpecWith a
after :: ActionWith a -> SpecWith a -> SpecWith a
after ActionWith a
action = (ActionWith a -> ActionWith a) -> SpecWith a -> SpecWith a
forall a b.
(ActionWith a -> ActionWith b) -> SpecWith a -> SpecWith b
aroundWith ((ActionWith a -> ActionWith a) -> SpecWith a -> SpecWith a)
-> (ActionWith a -> ActionWith a) -> SpecWith a -> SpecWith a
forall a b. (a -> b) -> a -> b
$ \ActionWith a
e a
x -> ActionWith a
e a
x IO () -> IO () -> IO ()
forall a b. IO a -> IO b -> IO a
`finally` ActionWith a
action a
x

-- | Run a custom action after every spec item.
after_ :: IO () -> SpecWith a -> SpecWith a
after_ :: IO () -> SpecWith a -> SpecWith a
after_ IO ()
action = ActionWith a -> SpecWith a -> SpecWith a
forall a. ActionWith a -> SpecWith a -> SpecWith a
after (ActionWith a -> SpecWith a -> SpecWith a)
-> ActionWith a -> SpecWith a -> SpecWith a
forall a b. (a -> b) -> a -> b
$ \a
_ -> IO ()
action

-- | Run a custom action before and/or after every spec item.
around :: (ActionWith a -> IO ()) -> SpecWith a -> Spec
around :: (ActionWith a -> IO ()) -> SpecWith a -> Spec
around ActionWith a -> IO ()
action = (ActionWith a -> ActionWith ()) -> SpecWith a -> Spec
forall a b.
(ActionWith a -> ActionWith b) -> SpecWith a -> SpecWith b
aroundWith ((ActionWith a -> ActionWith ()) -> SpecWith a -> Spec)
-> (ActionWith a -> ActionWith ()) -> SpecWith a -> Spec
forall a b. (a -> b) -> a -> b
$ \ActionWith a
e () -> ActionWith a -> IO ()
action ActionWith a
e

-- | Run a custom action after the last spec item.
afterAll :: ActionWith a -> SpecWith a -> SpecWith a
afterAll :: ActionWith a -> SpecWith a -> SpecWith a
afterAll ActionWith a
action SpecWith a
spec = IO [SpecTree a] -> SpecM a [SpecTree a]
forall r a. IO r -> SpecM a r
runIO (SpecWith a -> IO [SpecTree a]
forall a. SpecWith a -> IO [SpecTree a]
runSpecM SpecWith a
spec) SpecM a [SpecTree a] -> ([SpecTree a] -> SpecWith a) -> SpecWith a
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= [SpecTree a] -> SpecWith a
forall a. [SpecTree a] -> SpecWith a
fromSpecList ([SpecTree a] -> SpecWith a)
-> ([SpecTree a] -> [SpecTree a]) -> [SpecTree a] -> SpecWith a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SpecTree a -> [SpecTree a]
forall (m :: * -> *) a. Monad m => a -> m a
return (SpecTree a -> [SpecTree a])
-> ([SpecTree a] -> SpecTree a) -> [SpecTree a] -> [SpecTree a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ActionWith a -> [SpecTree a] -> SpecTree a
forall c a. c -> [Tree c a] -> Tree c a
NodeWithCleanup ActionWith a
action

-- | Run a custom action after the last spec item.
afterAll_ :: IO () -> SpecWith a -> SpecWith a
afterAll_ :: IO () -> SpecWith a -> SpecWith a
afterAll_ IO ()
action = ActionWith a -> SpecWith a -> SpecWith a
forall a. ActionWith a -> SpecWith a -> SpecWith a
afterAll (\a
_ -> IO ()
action)

-- | Run a custom action before and/or after every spec item.
around_ :: (IO () -> IO ()) -> SpecWith a -> SpecWith a
around_ :: (IO () -> IO ()) -> SpecWith a -> SpecWith a
around_ IO () -> IO ()
action = (ActionWith a -> ActionWith a) -> SpecWith a -> SpecWith a
forall a b.
(ActionWith a -> ActionWith b) -> SpecWith a -> SpecWith b
aroundWith ((ActionWith a -> ActionWith a) -> SpecWith a -> SpecWith a)
-> (ActionWith a -> ActionWith a) -> SpecWith a -> SpecWith a
forall a b. (a -> b) -> a -> b
$ \ActionWith a
e a
a -> IO () -> IO ()
action (ActionWith a
e a
a)

-- | Run a custom action before and/or after every spec item.
aroundWith :: (ActionWith a -> ActionWith b) -> SpecWith a -> SpecWith b
aroundWith :: (ActionWith a -> ActionWith b) -> SpecWith a -> SpecWith b
aroundWith ActionWith a -> ActionWith b
action = (ActionWith a -> ActionWith b)
-> (Item a -> Item b) -> SpecWith a -> SpecWith b
forall a b.
(ActionWith a -> ActionWith b)
-> (Item a -> Item b) -> SpecWith a -> SpecWith b
mapSpecItem ActionWith a -> ActionWith b
action ((ActionWith a -> ActionWith b) -> Item a -> Item b
forall a b. (ActionWith a -> ActionWith b) -> Item a -> Item b
modifyAroundAction ActionWith a -> ActionWith b
action)

modifyAroundAction :: (ActionWith a -> ActionWith b) -> Item a -> Item b
modifyAroundAction :: (ActionWith a -> ActionWith b) -> Item a -> Item b
modifyAroundAction ActionWith a -> ActionWith b
action item :: Item a
item@Item{itemExample :: forall a.
Item a
-> Params
-> (ActionWith a -> IO ())
-> ProgressCallback
-> IO Result
itemExample = Params -> (ActionWith a -> IO ()) -> ProgressCallback -> IO Result
e} =
  Item a
item{ itemExample :: Params -> (ActionWith b -> IO ()) -> ProgressCallback -> IO Result
itemExample = \Params
params ActionWith b -> IO ()
aroundAction -> Params -> (ActionWith a -> IO ()) -> ProgressCallback -> IO Result
e Params
params (ActionWith b -> IO ()
aroundAction (ActionWith b -> IO ())
-> (ActionWith a -> ActionWith b) -> ActionWith a -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ActionWith a -> ActionWith b
action) }