{-# LANGUAGE FlexibleInstances #-}
{-# OPTIONS_GHC -Wno-orphans #-}

-- |
--
-- This module provides a monad transformer, 'MockT', which can be used to test
-- with mocks of Haskell @mtl@-style type classes.  To use a mock, you define
-- the expected actions and their results, and then run the code you are
-- testing.  The framework verifies that the behavior of the code matched your
-- expectations.
--
-- For an introduction to the idea of mocks, see
-- <https://martinfowler.com/articles/mocksArentStubs.html Mocks Aren't Stubs>,
-- by Martin Fowler.
--
-- WARNING: Hmock's API is likely to change soon.  Please ensure you use an
-- upper bound on the version number.  The current API works fine for mocking
-- with MTL-style classes.  I want HMock to also work with effect systems,
-- servant, haxl, and more.  To accomplish this, I'll need to make breaking
-- changes to the API.
--
-- Suppose you have a @MonadFilesystem@ typeclass, which is instantiated by
-- monads that implement filesystem operations:
--
-- @
-- class 'Monad' m => MonadFilesystem m where
--   readFile :: 'FilePath' -> m 'String'
--   writeFile :: 'FilePath' -> 'String' -> m ()
-- @
--
-- You can use HMock to test code using @MonadFilesystem@ like this:
--
-- @
-- copyFile :: MonadFilesystem m => 'FilePath' -> 'FilePath' -> m ()
-- copyFile a b = readFile a >>= writeFile b
--
-- 'Test.HMock.TH.makeMockable' ''MonadFilesystem
--
-- spec = describe "copyFile" '$'
--   it "reads a file and writes its contents to another file" '$'
--     'runMockT' '$' do
--       'expect' '$' ReadFile "foo.txt" '|->' "contents"
--       'expect' '$' WriteFile "bar.txt" "contents" '|->' ()
--       copyFile "foo.txt" "bar.txt"
-- @
--
-- The Template Haskell splice, 'Test.HMock.TH.makeMockable', generates the
-- boilerplate needed to use @MonadFilesystem@ with HMock.  You then use
-- 'runMockT' to begin a test with mocks, 'expect' to set up your expected
-- actions and responses, and finally execute your code.
module Test.HMock
  ( 
    -- * The 'Mockable' class

    -- | HMock starts with the 'Mockable' class (most of which is actually in
    -- its superclass, 'MockableBase').  This class is implemented for each
    -- interface you want to mock, and describes which actions are possible,
    -- and how to match and compare them.  It's a lot of boilerplate, so you'll
    -- usually derive it with Template Haskell, but the instance must exist.
    module Test.HMock.Mockable,

    -- * Running mocks

    -- | Tests with mocks run in the 'MockT' monad transformer, which wraps a
    -- base monad and adds the ability to delegate methods to HMock for
    -- matching.  'runMockT' is the entry point for 'MockT'.
    --
    -- This module also defines the more restricted 'MockSetup;' monad, which
    -- is used to set up defaults for a type.
    module Test.HMock.MockT,

    -- * Rules for actions and responses

    -- | The bread and butter of mocks is matching actions and specifying
    -- responses.  Matchers and corresponding responses are combined into a
    -- 'Rule'
    module Test.HMock.Rule,

    -- * Combinators for building test plans

    -- | A complete execution plans consists of a collection of individual rules
    -- combined in various ways.  HMock defines a set of composable combinators
    -- for the execution plan.
    module Test.HMock.ExpectContext,

    -- * Predicates

    -- | When matching methods, you can either match on exact parameter values,
    -- or using 'Predicate's.  The 'Predicate' type either accepts or rejects
    -- values of parameters, and HMock provides an extensive library of standard
    -- 'Predicate's to make it easier to match exactly what you want to.
    module Test.HMock.Predicates,

    -- * Multiplicity

    -- | For repeated actions in your execution plan, you often want to control
    -- the number of times somrthing is allowed to happen.  This is called a
    -- 'Multiplicity'.
    module Test.HMock.Multiplicity,

    -- * Delegating mocks

    -- | In order to run your test code with the 'MockT', you need instances of
    -- your effect classes for the 'MockT' type.  If you mock all methods of the
    -- class, this can be derived using Template Haskell.  For partial mocks,
    -- you'll need to write the instances yourself, using 'mockMethod' and its
    -- cousin 'mockDefaultlessMethod'.
    module Test.HMock.MockMethod,

    -- * Template Haskell generator

    -- | These are the Template Haskell splices which generate boilerplate for
    -- your classes to be used with HMock.
    module Test.HMock.TH,
  )
where

import Test.HMock.ExpectContext
import Test.HMock.MockT
import Test.HMock.MockMethod
import Test.HMock.Mockable
import Test.HMock.Multiplicity
import Test.HMock.Predicates
import Test.HMock.Rule
import Test.HMock.TH