{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE StandaloneDeriving #-}

module Test.HMock.Internal.Expectable where

import Control.Monad.Trans (MonadIO)
import Data.Kind (Constraint, Type)
import Data.Maybe (listToMaybe)
import Data.Typeable
import GHC.Stack (CallStack, HasCallStack, callStack)
import GHC.TypeLits (KnownSymbol, Symbol)
import Test.HMock.Internal.ExpectSet
import {-# SOURCE #-} Test.HMock.Internal.MockT
import Test.HMock.Internal.Mockable
import Test.HMock.Internal.Multiplicity
import Test.HMock.Internal.Util (Located (Loc), locate, withLoc)

-- | Something that can be expected.  This type class covers a number of cases:
--
--   * Expecting an exact 'Action'.
--   * Expecting anything that matches a 'Matcher'.
--   * Adding a return value (with '|->') or response (with '|=>').
class Expectable cls name m r e | e -> cls name m r where
  toRule :: e -> Rule cls name m r

-- | A rule for matching a method and responding to it when it matches.
--
-- The method may be matched by providing either an 'Action' to match exactly,
-- or a 'Matcher'.  Exact matching is only available when all method arguments
--
--
-- A 'Rule' may have zero or more responses, which are attached using '|->' and
-- '|=>'.  If there are no responses for a 'Rule', then there must be a default
-- response for that action, and it is used.  If more than one response is
-- added, the rule will perform the responses in order, repeating the last
-- response if there are additional matches.
--
-- Example:
--
-- @
-- 'expect' $
--   GetLine_ 'Test.HMock.anything'
--     '|->' "hello"
--     '|=>' \(GetLine prompt) -> "The prompt was " ++ prompt
--     '|->' "quit"
-- @
data
  Rule
    (cls :: (Type -> Type) -> Constraint)
    (name :: Symbol)
    (m :: Type -> Type)
    (r :: Type)
  where
  (:=>) ::
    Matcher cls name m r ->
    [Action cls name m r -> MockT m r] ->
    Rule cls name m r

-- | Attaches a response to an expectation.  This is a very flexible response,
-- which can look at arguments, do things in the base monad, set up more
-- expectations, etc.  The matching 'Action' is passed to the response, and is
-- guaranteed to be a match so it's fine to just pattern match on the correct
-- method.
(|=>) ::
  Expectable cls name m r expectable =>
  expectable ->
  (Action cls name m r -> MockT m r) ->
  Rule cls name m r
expectable
e |=> :: expectable
-> (Action cls name m r -> MockT m r) -> Rule cls name m r
|=> Action cls name m r -> MockT m r
r = Matcher cls name m r
m Matcher cls name m r
-> [Action cls name m r -> MockT m r] -> Rule cls name m r
forall (cls :: (* -> *) -> Constraint) (name :: Symbol)
       (m :: * -> *) r.
Matcher cls name m r
-> [Action cls name m r -> MockT m r] -> Rule cls name m r
:=> ([Action cls name m r -> MockT m r]
rs [Action cls name m r -> MockT m r]
-> [Action cls name m r -> MockT m r]
-> [Action cls name m r -> MockT m r]
forall a. [a] -> [a] -> [a]
++ [Action cls name m r -> MockT m r
r]) where Matcher cls name m r
m :=> [Action cls name m r -> MockT m r]
rs = expectable -> Rule cls name m r
forall (cls :: (* -> *) -> Constraint) (name :: Symbol)
       (m :: * -> *) r e.
Expectable cls name m r e =>
e -> Rule cls name m r
toRule expectable
e

infixl 1 |=>

-- | Attaches a return value to an expectation.  This is more convenient than
-- '|=>' in the common case where you just want to return a known result.
-- @e '|->' r@ means the same thing as @e '|=>' 'const' ('return' r)@.
(|->) ::
  (Monad m, Expectable cls name m r expectable) =>
  expectable ->
  r ->
  Rule cls name m r
expectable
m |-> :: expectable -> r -> Rule cls name m r
|-> r
r = expectable
m expectable
-> (Action cls name m r -> MockT m r) -> Rule cls name m r
forall (cls :: (* -> *) -> Constraint) (name :: Symbol)
       (m :: * -> *) r expectable.
Expectable cls name m r expectable =>
expectable
-> (Action cls name m r -> MockT m r) -> Rule cls name m r
|=> MockT m r -> Action cls name m r -> MockT m r
forall a b. a -> b -> a
const (r -> MockT m r
forall (m :: * -> *) a. Monad m => a -> m a
return r
r)

infixl 1 |->

instance Expectable cls name m r (Rule cls name m r) where
  toRule :: Rule cls name m r -> Rule cls name m r
toRule = Rule cls name m r -> Rule cls name m r
forall a. a -> a
id

instance Expectable cls name m r (Matcher cls name m r) where
  toRule :: Matcher cls name m r -> Rule cls name m r
toRule Matcher cls name m r
m = Matcher cls name m r
m Matcher cls name m r
-> [Action cls name m r -> MockT m r] -> Rule cls name m r
forall (cls :: (* -> *) -> Constraint) (name :: Symbol)
       (m :: * -> *) r.
Matcher cls name m r
-> [Action cls name m r -> MockT m r] -> Rule cls name m r
:=> []

-- | All constraints needed to mock a method with the given class, name, base
-- monad, and return type.
type MockableMethod
  (cls :: (Type -> Type) -> Constraint)
  (name :: Symbol)
  (m :: Type -> Type)
  (r :: Type) =
  (Mockable cls, Typeable m, KnownSymbol name, Typeable r)

-- | A Rule that contains only a single response.  This is the target for
-- desugaring the multi-response rule format.
data
  SingleRule
    (cls :: (Type -> Type) -> Constraint)
    (name :: Symbol)
    (m :: Type -> Type)
    (r :: Type)
  where
  (:->) ::
    Matcher cls name m r ->
    Maybe (Action cls name m r -> MockT m r) ->
    SingleRule cls name m r

-- | A single step of an expectation.
data Step m where
  Step ::
    MockableMethod cls name m r =>
    Located (SingleRule cls name m r) ->
    Step m

instance Show (Step m) where
  show :: Step m -> String
show (Step l :: Located (SingleRule cls name m r)
l@(Loc Maybe String
_ (Matcher cls name m r
m :-> Maybe (Action cls name m r -> MockT m r)
_))) =
    Located String -> String
withLoc (Maybe (Action cls name m Any) -> Matcher cls name m r -> String
forall (cls :: (* -> *) -> Constraint) (name :: Symbol)
       (m :: * -> *) a b.
MockableBase cls =>
Maybe (Action cls name m a) -> Matcher cls name m b -> String
showMatcher Maybe (Action cls name m Any)
forall a. Maybe a
Nothing Matcher cls name m r
m String -> Located (SingleRule cls name m r) -> Located String
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ Located (SingleRule cls name m r)
l)

-- | Expands a Rule into an expectation.  The expected multiplicity will be one
-- if there are no responses; otherwise one call is expected per response.
expandRule ::
  MockableMethod cls name m r =>
  CallStack ->
  Rule cls name m r ->
  ExpectSet (Step m)
expandRule :: CallStack -> Rule cls name m r -> ExpectSet (Step m)
expandRule CallStack
callstack (Matcher cls name m r
m :=> []) =
  Step m -> ExpectSet (Step m)
forall step. step -> ExpectSet step
ExpectStep (Located (SingleRule cls name m r) -> Step m
forall (cls :: (* -> *) -> Constraint) (name :: Symbol)
       (m :: * -> *) r.
MockableMethod cls name m r =>
Located (SingleRule cls name m r) -> Step m
Step (CallStack
-> SingleRule cls name m r -> Located (SingleRule cls name m r)
forall a. CallStack -> a -> Located a
locate CallStack
callstack (Matcher cls name m r
m Matcher cls name m r
-> Maybe (Action cls name m r -> MockT m r)
-> SingleRule cls name m r
forall (cls :: (* -> *) -> Constraint) (name :: Symbol)
       (m :: * -> *) r.
Matcher cls name m r
-> Maybe (Action cls name m r -> MockT m r)
-> SingleRule cls name m r
:-> Maybe (Action cls name m r -> MockT m r)
forall a. Maybe a
Nothing)))
expandRule CallStack
callstack (Matcher cls name m r
m :=> [Action cls name m r -> MockT m r]
rs) =
  (ExpectSet (Step m) -> ExpectSet (Step m) -> ExpectSet (Step m))
-> [ExpectSet (Step m)] -> ExpectSet (Step m)
forall (t :: * -> *) a. Foldable t => (a -> a -> a) -> t a -> a
foldr1
    ExpectSet (Step m) -> ExpectSet (Step m) -> ExpectSet (Step m)
forall step. ExpectSet step -> ExpectSet step -> ExpectSet step
ExpectInterleave
    (((Action cls name m r -> MockT m r) -> ExpectSet (Step m))
-> [Action cls name m r -> MockT m r] -> [ExpectSet (Step m)]
forall a b. (a -> b) -> [a] -> [b]
map (Step m -> ExpectSet (Step m)
forall step. step -> ExpectSet step
ExpectStep (Step m -> ExpectSet (Step m))
-> ((Action cls name m r -> MockT m r) -> Step m)
-> (Action cls name m r -> MockT m r)
-> ExpectSet (Step m)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Located (SingleRule cls name m r) -> Step m
forall (cls :: (* -> *) -> Constraint) (name :: Symbol)
       (m :: * -> *) r.
MockableMethod cls name m r =>
Located (SingleRule cls name m r) -> Step m
Step (Located (SingleRule cls name m r) -> Step m)
-> ((Action cls name m r -> MockT m r)
    -> Located (SingleRule cls name m r))
-> (Action cls name m r -> MockT m r)
-> Step m
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CallStack
-> SingleRule cls name m r -> Located (SingleRule cls name m r)
forall a. CallStack -> a -> Located a
locate CallStack
callstack (SingleRule cls name m r -> Located (SingleRule cls name m r))
-> ((Action cls name m r -> MockT m r) -> SingleRule cls name m r)
-> (Action cls name m r -> MockT m r)
-> Located (SingleRule cls name m r)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Matcher cls name m r
m Matcher cls name m r
-> Maybe (Action cls name m r -> MockT m r)
-> SingleRule cls name m r
forall (cls :: (* -> *) -> Constraint) (name :: Symbol)
       (m :: * -> *) r.
Matcher cls name m r
-> Maybe (Action cls name m r -> MockT m r)
-> SingleRule cls name m r
:->) (Maybe (Action cls name m r -> MockT m r)
 -> SingleRule cls name m r)
-> ((Action cls name m r -> MockT m r)
    -> Maybe (Action cls name m r -> MockT m r))
-> (Action cls name m r -> MockT m r)
-> SingleRule cls name m r
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Action cls name m r -> MockT m r)
-> Maybe (Action cls name m r -> MockT m r)
forall a. a -> Maybe a
Just) [Action cls name m r -> MockT m r]
rs)

-- | Expands a Rule into an expectation, given a target multiplicity.  It is an
-- error if there are too many responses for the multiplicity.  If there are
-- too few responses, the last response will be repeated.
expandRepeatRule ::
  MockableMethod cls name m r =>
  Multiplicity ->
  CallStack ->
  Rule cls name m r ->
  ExpectSet (Step m)
expandRepeatRule :: Multiplicity
-> CallStack -> Rule cls name m r -> ExpectSet (Step m)
expandRepeatRule Multiplicity
mult CallStack
_ (Matcher cls name m r
_ :=> [Action cls name m r -> MockT m r]
rs)
  | Bool -> Bool
not (Multiplicity -> Bool
feasible (Multiplicity
mult Multiplicity -> Multiplicity -> Multiplicity
forall a. Num a => a -> a -> a
- Int -> Multiplicity
forall a b. (Integral a, Num b) => a -> b
fromIntegral ([Action cls name m r -> MockT m r] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Action cls name m r -> MockT m r]
rs))) =
    String -> ExpectSet (Step m)
forall a. HasCallStack => String -> a
error (String -> ExpectSet (Step m)) -> String -> ExpectSet (Step m)
forall a b. (a -> b) -> a -> b
$
      Int -> String
forall a. Show a => a -> String
show ([Action cls name m r -> MockT m r] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Action cls name m r -> MockT m r]
rs)
        String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" responses is too many for multiplicity "
        String -> ShowS
forall a. [a] -> [a] -> [a]
++ Multiplicity -> String
forall a. Show a => a -> String
show Multiplicity
mult
expandRepeatRule Multiplicity
mult CallStack
callstack (Matcher cls name m r
m :=> (Action cls name m r -> MockT m r
r1 : Action cls name m r -> MockT m r
r2 : [Action cls name m r -> MockT m r]
rs)) =
  ExpectSet (Step m) -> ExpectSet (Step m) -> ExpectSet (Step m)
forall step. ExpectSet step -> ExpectSet step -> ExpectSet step
ExpectSequence ExpectSet (Step m)
first (Multiplicity
-> CallStack -> Rule cls name m r -> ExpectSet (Step m)
forall (cls :: (* -> *) -> Constraint) (name :: Symbol)
       (m :: * -> *) r.
MockableMethod cls name m r =>
Multiplicity
-> CallStack -> Rule cls name m r -> ExpectSet (Step m)
expandRepeatRule (Multiplicity
mult Multiplicity -> Multiplicity -> Multiplicity
forall a. Num a => a -> a -> a
- Multiplicity
1) CallStack
callstack (Matcher cls name m r
m Matcher cls name m r
-> [Action cls name m r -> MockT m r] -> Rule cls name m r
forall (cls :: (* -> *) -> Constraint) (name :: Symbol)
       (m :: * -> *) r.
Matcher cls name m r
-> [Action cls name m r -> MockT m r] -> Rule cls name m r
:=> (Action cls name m r -> MockT m r
r2 (Action cls name m r -> MockT m r)
-> [Action cls name m r -> MockT m r]
-> [Action cls name m r -> MockT m r]
forall a. a -> [a] -> [a]
: [Action cls name m r -> MockT m r]
rs)))
  where
    first :: ExpectSet (Step m)
first
      | Multiplicity -> Bool
exhaustable Multiplicity
mult =
        Multiplicity -> ExpectSet (Step m) -> ExpectSet (Step m)
forall step. Multiplicity -> ExpectSet step -> ExpectSet step
ExpectMulti
          (Multiplicity -> Multiplicity
atMost Multiplicity
1)
          (Step m -> ExpectSet (Step m)
forall step. step -> ExpectSet step
ExpectStep (Located (SingleRule cls name m r) -> Step m
forall (cls :: (* -> *) -> Constraint) (name :: Symbol)
       (m :: * -> *) r.
MockableMethod cls name m r =>
Located (SingleRule cls name m r) -> Step m
Step (CallStack
-> SingleRule cls name m r -> Located (SingleRule cls name m r)
forall a. CallStack -> a -> Located a
locate CallStack
callstack (Matcher cls name m r
m Matcher cls name m r
-> Maybe (Action cls name m r -> MockT m r)
-> SingleRule cls name m r
forall (cls :: (* -> *) -> Constraint) (name :: Symbol)
       (m :: * -> *) r.
Matcher cls name m r
-> Maybe (Action cls name m r -> MockT m r)
-> SingleRule cls name m r
:-> (Action cls name m r -> MockT m r)
-> Maybe (Action cls name m r -> MockT m r)
forall a. a -> Maybe a
Just Action cls name m r -> MockT m r
r1))))
      | Bool
otherwise = Step m -> ExpectSet (Step m)
forall step. step -> ExpectSet step
ExpectStep (Located (SingleRule cls name m r) -> Step m
forall (cls :: (* -> *) -> Constraint) (name :: Symbol)
       (m :: * -> *) r.
MockableMethod cls name m r =>
Located (SingleRule cls name m r) -> Step m
Step (CallStack
-> SingleRule cls name m r -> Located (SingleRule cls name m r)
forall a. CallStack -> a -> Located a
locate CallStack
callstack (Matcher cls name m r
m Matcher cls name m r
-> Maybe (Action cls name m r -> MockT m r)
-> SingleRule cls name m r
forall (cls :: (* -> *) -> Constraint) (name :: Symbol)
       (m :: * -> *) r.
Matcher cls name m r
-> Maybe (Action cls name m r -> MockT m r)
-> SingleRule cls name m r
:-> (Action cls name m r -> MockT m r)
-> Maybe (Action cls name m r -> MockT m r)
forall a. a -> Maybe a
Just Action cls name m r -> MockT m r
r1)))
expandRepeatRule Multiplicity
mult CallStack
callstack (Matcher cls name m r
m :=> [Action cls name m r -> MockT m r]
rs) =
  Multiplicity -> ExpectSet (Step m) -> ExpectSet (Step m)
forall step. Multiplicity -> ExpectSet step -> ExpectSet step
ExpectMulti Multiplicity
mult (Step m -> ExpectSet (Step m)
forall step. step -> ExpectSet step
ExpectStep (Located (SingleRule cls name m r) -> Step m
forall (cls :: (* -> *) -> Constraint) (name :: Symbol)
       (m :: * -> *) r.
MockableMethod cls name m r =>
Located (SingleRule cls name m r) -> Step m
Step (CallStack
-> SingleRule cls name m r -> Located (SingleRule cls name m r)
forall a. CallStack -> a -> Located a
locate CallStack
callstack (Matcher cls name m r
m Matcher cls name m r
-> Maybe (Action cls name m r -> MockT m r)
-> SingleRule cls name m r
forall (cls :: (* -> *) -> Constraint) (name :: Symbol)
       (m :: * -> *) r.
Matcher cls name m r
-> Maybe (Action cls name m r -> MockT m r)
-> SingleRule cls name m r
:-> [Action cls name m r -> MockT m r]
-> Maybe (Action cls name m r -> MockT m r)
forall a. [a] -> Maybe a
listToMaybe [Action cls name m r -> MockT m r]
rs))))

-- | Type class for contexts in which it makes sense to express an expectation.
-- Notably, this includes `MockT`, which expects actions to be performed during
-- a test.
class ExpectContext (t :: (Type -> Type) -> Type -> Type) where
  fromExpectSet :: MonadIO m => ExpectSet (Step m) -> t m ()

newtype Expected m a = Expected {Expected m a -> ExpectSet (Step m)
unwrapExpected :: ExpectSet (Step m)}

instance ExpectContext Expected where
  fromExpectSet :: ExpectSet (Step m) -> Expected m ()
fromExpectSet = ExpectSet (Step m) -> Expected m ()
forall (m :: * -> *) a. ExpectSet (Step m) -> Expected m a
Expected

-- | Creates an expectation that an action is performed once per given
-- response (or exactly once if there is no response).
--
-- @
-- 'runMockT' '$' do
--   'expect' '$'
--     ReadFile "foo.txt"
--       '|->' "lorem ipsum"
--       '|->' "oops, the file changed out from under me!"
--   callCodeUnderTest
-- @
--
-- In this example, `readFile` must be called exactly twice by the tested code,
-- and will return "lorem ipsum" the first time, but something different the
-- second time.
expect ::
  ( HasCallStack,
    MonadIO m,
    MockableMethod cls name m r,
    Expectable cls name m r expectable,
    ExpectContext ctx
  ) =>
  expectable ->
  ctx m ()
expect :: expectable -> ctx m ()
expect expectable
e = ExpectSet (Step m) -> ctx m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *).
(ExpectContext t, MonadIO m) =>
ExpectSet (Step m) -> t m ()
fromExpectSet (CallStack -> Rule cls name m r -> ExpectSet (Step m)
forall (cls :: (* -> *) -> Constraint) (name :: Symbol)
       (m :: * -> *) r.
MockableMethod cls name m r =>
CallStack -> Rule cls name m r -> ExpectSet (Step m)
expandRule CallStack
HasCallStack => CallStack
callStack (expectable -> Rule cls name m r
forall (cls :: (* -> *) -> Constraint) (name :: Symbol)
       (m :: * -> *) r e.
Expectable cls name m r e =>
e -> Rule cls name m r
toRule expectable
e))

-- | Creates an expectation that an action is performed some number of times.
--
-- @
--   'runMockT' '$' do
--     'expect' '$' MakeList
--     'expectN' ('Test.HMock.atLeast' 2) '$'
--       CheckList "Cindy Lou Who" '|->' "nice"
--
--     callCodeUnderTest
-- @
expectN ::
  ( HasCallStack,
    MonadIO m,
    MockableMethod cls name m r,
    Expectable cls name m r expectable,
    ExpectContext ctx
  ) =>
  -- | The number of times the action should be performed.
  Multiplicity ->
  -- | The action and its response.
  expectable ->
  ctx m ()
expectN :: Multiplicity -> expectable -> ctx m ()
expectN Multiplicity
mult expectable
e = ExpectSet (Step m) -> ctx m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *).
(ExpectContext t, MonadIO m) =>
ExpectSet (Step m) -> t m ()
fromExpectSet (Multiplicity
-> CallStack -> Rule cls name m r -> ExpectSet (Step m)
forall (cls :: (* -> *) -> Constraint) (name :: Symbol)
       (m :: * -> *) r.
MockableMethod cls name m r =>
Multiplicity
-> CallStack -> Rule cls name m r -> ExpectSet (Step m)
expandRepeatRule Multiplicity
mult CallStack
HasCallStack => CallStack
callStack (expectable -> Rule cls name m r
forall (cls :: (* -> *) -> Constraint) (name :: Symbol)
       (m :: * -> *) r e.
Expectable cls name m r e =>
e -> Rule cls name m r
toRule expectable
e))

-- | Specifies a response if a matching action is performed, but doesn't expect
-- anything.  This is equivalent to @'expectN' 'anyMultiplicity'@, but shorter.
--
-- In this example, the later use of 'whenever' overrides earlier uses, but only
-- for calls that match its conditions.
--
-- @
--   'runMockT' '$' do
--     'whenever' '$' ReadFile_ anything '|->' "tlhIngan maH!"
--     'whenever' '$' ReadFile "config.txt" '|->' "lang: klingon"
--
--     callCodeUnderTest
-- @
expectAny ::
  ( HasCallStack,
    MonadIO m,
    MockableMethod cls name m r,
    Expectable cls name m r expectable,
    ExpectContext ctx
  ) =>
  expectable ->
  ctx m ()
expectAny :: expectable -> ctx m ()
expectAny expectable
e =
  ExpectSet (Step m) -> ctx m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *).
(ExpectContext t, MonadIO m) =>
ExpectSet (Step m) -> t m ()
fromExpectSet (Multiplicity
-> CallStack -> Rule cls name m r -> ExpectSet (Step m)
forall (cls :: (* -> *) -> Constraint) (name :: Symbol)
       (m :: * -> *) r.
MockableMethod cls name m r =>
Multiplicity
-> CallStack -> Rule cls name m r -> ExpectSet (Step m)
expandRepeatRule Multiplicity
anyMultiplicity CallStack
HasCallStack => CallStack
callStack (expectable -> Rule cls name m r
forall (cls :: (* -> *) -> Constraint) (name :: Symbol)
       (m :: * -> *) r e.
Expectable cls name m r e =>
e -> Rule cls name m r
toRule expectable
e))

-- | Creates a sequential expectation.  Other actions can still happen during
-- the sequence, but these specific expectations must be met in this order.
--
-- @
--   'inSequence'
--     [ 'expect' '$' MoveForward,
--       'expect' '$' TurnRight,
--       'expect' '$' MoveForward
--     ]
-- @
--
-- Beware of using 'inSequence' too often.  It is appropriate when the property
-- you are testing is that the order of effects is correct.  If that's not the
-- purpose of the test, consider adding several independent expectations,
-- instead.  This avoids over-asserting, and keeps your tests less brittle.
inSequence ::
  (MonadIO m, ExpectContext ctx) =>
  (forall ctx'. ExpectContext ctx' => [ctx' m ()]) ->
  ctx m ()
inSequence :: (forall (ctx' :: (* -> *) -> * -> *).
 ExpectContext ctx' =>
 [ctx' m ()])
-> ctx m ()
inSequence forall (ctx' :: (* -> *) -> * -> *).
ExpectContext ctx' =>
[ctx' m ()]
es = ExpectSet (Step m) -> ctx m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *).
(ExpectContext t, MonadIO m) =>
ExpectSet (Step m) -> t m ()
fromExpectSet ((ExpectSet (Step m) -> ExpectSet (Step m) -> ExpectSet (Step m))
-> [ExpectSet (Step m)] -> ExpectSet (Step m)
forall (t :: * -> *) a. Foldable t => (a -> a -> a) -> t a -> a
foldr1 ExpectSet (Step m) -> ExpectSet (Step m) -> ExpectSet (Step m)
forall step. ExpectSet step -> ExpectSet step -> ExpectSet step
ExpectSequence ((Expected m () -> ExpectSet (Step m))
-> [Expected m ()] -> [ExpectSet (Step m)]
forall a b. (a -> b) -> [a] -> [b]
map Expected m () -> ExpectSet (Step m)
forall (m :: * -> *) a. Expected m a -> ExpectSet (Step m)
unwrapExpected [Expected m ()]
forall (ctx' :: (* -> *) -> * -> *).
ExpectContext ctx' =>
[ctx' m ()]
es))

-- | Combines multiple expectations, which can occur in any order.  Most of the
-- time, you can achieve the same thing by expecting each separately, but this
-- can be combined in complex expectations to describe more complex ordering
-- constraints.
--
-- @
--   'inSequence'
--     [ 'inAnyOrder'
--         [ 'expect' '$' AdjustMirrors,
--           'expect' '$' FastenSeatBelt
--         ],
--       'expect' '$' StartCar
--     ]
-- @
inAnyOrder ::
  (MonadIO m, ExpectContext ctx) =>
  (forall ctx'. ExpectContext ctx' => [ctx' m ()]) ->
  ctx m ()
inAnyOrder :: (forall (ctx' :: (* -> *) -> * -> *).
 ExpectContext ctx' =>
 [ctx' m ()])
-> ctx m ()
inAnyOrder forall (ctx' :: (* -> *) -> * -> *).
ExpectContext ctx' =>
[ctx' m ()]
es = ExpectSet (Step m) -> ctx m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *).
(ExpectContext t, MonadIO m) =>
ExpectSet (Step m) -> t m ()
fromExpectSet ((ExpectSet (Step m) -> ExpectSet (Step m) -> ExpectSet (Step m))
-> [ExpectSet (Step m)] -> ExpectSet (Step m)
forall (t :: * -> *) a. Foldable t => (a -> a -> a) -> t a -> a
foldr1 ExpectSet (Step m) -> ExpectSet (Step m) -> ExpectSet (Step m)
forall step. ExpectSet step -> ExpectSet step -> ExpectSet step
ExpectInterleave ((Expected m () -> ExpectSet (Step m))
-> [Expected m ()] -> [ExpectSet (Step m)]
forall a b. (a -> b) -> [a] -> [b]
map Expected m () -> ExpectSet (Step m)
forall (m :: * -> *) a. Expected m a -> ExpectSet (Step m)
unwrapExpected [Expected m ()]
forall (ctx' :: (* -> *) -> * -> *).
ExpectContext ctx' =>
[ctx' m ()]
es))

-- | Combines multiple expectations, requiring exactly one of them to occur.
--
-- @
--   'anyOf'
--     [ 'expect' $ ApplyForJob,
--       'expect' $ ApplyForUniversity
--     ]
-- @
anyOf ::
  (MonadIO m, ExpectContext ctx) =>
  (forall ctx'. ExpectContext ctx' => [ctx' m ()]) ->
  ctx m ()
anyOf :: (forall (ctx' :: (* -> *) -> * -> *).
 ExpectContext ctx' =>
 [ctx' m ()])
-> ctx m ()
anyOf forall (ctx' :: (* -> *) -> * -> *).
ExpectContext ctx' =>
[ctx' m ()]
es = ExpectSet (Step m) -> ctx m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *).
(ExpectContext t, MonadIO m) =>
ExpectSet (Step m) -> t m ()
fromExpectSet ((ExpectSet (Step m) -> ExpectSet (Step m) -> ExpectSet (Step m))
-> [ExpectSet (Step m)] -> ExpectSet (Step m)
forall (t :: * -> *) a. Foldable t => (a -> a -> a) -> t a -> a
foldr1 ExpectSet (Step m) -> ExpectSet (Step m) -> ExpectSet (Step m)
forall step. ExpectSet step -> ExpectSet step -> ExpectSet step
ExpectEither ((Expected m () -> ExpectSet (Step m))
-> [Expected m ()] -> [ExpectSet (Step m)]
forall a b. (a -> b) -> [a] -> [b]
map Expected m () -> ExpectSet (Step m)
forall (m :: * -> *) a. Expected m a -> ExpectSet (Step m)
unwrapExpected [Expected m ()]
forall (ctx' :: (* -> *) -> * -> *).
ExpectContext ctx' =>
[ctx' m ()]
es))

-- | Creates a parent expectation that the child expectation will happen a
-- certain number of times.  Unlike `expectN`, the child expectation can be
-- arbitrarily complex and span multiple actions.  Also unlike 'expectN', each
-- new execution will restart response sequences for rules with more than one
-- response.
--
-- Different occurrences of the child can be interleaved.  In case of ambiguity,
-- progressing on an existing occurrence is preferred over starting a new
-- occurrence.
times :: (MonadIO m, ExpectContext ctx) =>
  Multiplicity ->
  (forall ctx'. ExpectContext ctx' => ctx' m ()) ->
  ctx m ()
times :: Multiplicity
-> (forall (ctx' :: (* -> *) -> * -> *).
    ExpectContext ctx' =>
    ctx' m ())
-> ctx m ()
times Multiplicity
mult forall (ctx' :: (* -> *) -> * -> *).
ExpectContext ctx' =>
ctx' m ()
e = ExpectSet (Step m) -> ctx m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *).
(ExpectContext t, MonadIO m) =>
ExpectSet (Step m) -> t m ()
fromExpectSet (Multiplicity -> ExpectSet (Step m) -> ExpectSet (Step m)
forall step. Multiplicity -> ExpectSet step -> ExpectSet step
ExpectMulti Multiplicity
mult (Expected m () -> ExpectSet (Step m)
forall (m :: * -> *) a. Expected m a -> ExpectSet (Step m)
unwrapExpected Expected m ()
forall (ctx' :: (* -> *) -> * -> *).
ExpectContext ctx' =>
ctx' m ()
e))

-- | Creates a parent expectation that the child expectation will happen a
-- certain number of times.  Unlike `expectN`, the child expectation can be
-- arbitrarily complex and span multiple actions.  Also unlike 'expectN', each
-- new execution will restart response sequences for rules with more than one
-- response.
--
-- Different occurrences of the child must happen consecutively, with one
-- finishing before the next begins.
consecutiveTimes :: (MonadIO m, ExpectContext ctx) =>
  Multiplicity ->
  (forall ctx'. ExpectContext ctx' => ctx' m ()) ->
  ctx m ()
consecutiveTimes :: Multiplicity
-> (forall (ctx' :: (* -> *) -> * -> *).
    ExpectContext ctx' =>
    ctx' m ())
-> ctx m ()
consecutiveTimes Multiplicity
mult forall (ctx' :: (* -> *) -> * -> *).
ExpectContext ctx' =>
ctx' m ()
e =
  ExpectSet (Step m) -> ctx m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *).
(ExpectContext t, MonadIO m) =>
ExpectSet (Step m) -> t m ()
fromExpectSet (Multiplicity -> ExpectSet (Step m) -> ExpectSet (Step m)
forall step. Multiplicity -> ExpectSet step -> ExpectSet step
ExpectConsecutive Multiplicity
mult (Expected m () -> ExpectSet (Step m)
forall (m :: * -> *) a. Expected m a -> ExpectSet (Step m)
unwrapExpected Expected m ()
forall (ctx' :: (* -> *) -> * -> *).
ExpectContext ctx' =>
ctx' m ()
e))