-- | A manual mutation testing framework for monads and transformers.

{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}

module Test.Mutants where

import Control.Applicative
import Control.Monad
import Control.Monad.Trans
import Control.Monad.State
import Control.Monad.Reader
import Control.Monad.Except
import Control.Monad.Writer
import Data.Kind (Type)
import Test.QuickCheck (Arbitrary)
import Test.QuickCheck.HigherOrder (TestEq(..), Constructible(..))

-- | The type @Mutant v t m a@ is isomorphic to @t m a@, and inherits
-- various instances from it.
--
-- > class MyClass m where
-- >   myMethod :: m ()
-- >
-- > deriving instance MyClass (t m) => MyClass (Mutant v t m)
--
-- The first parameter @v@ is phantom, and identifies a "mutation".
--
-- A new mutation can be declared as a datatype.
--
-- > data BugInMyClass
--
-- Use overlapping instances to "mutate" an implementation.
--
-- > instance {-# OVERLAPPING #-} MyClass (Mutant BugInMyClass t m) where
-- >   myMethod = wrongMethod
newtype Mutant v t (m :: Type -> Type) a = Mutant { mutate :: t m a }
  deriving (
    Eq, Ord, Show,
    Functor, Applicative, Alternative, Monad, MonadPlus,
    MonadState s, MonadReader r, MonadError e, MonadWriter w,
    Arbitrary)

deriving instance MonadTrans t => MonadTrans (Mutant v t)
deriving instance TestEq (t m a) => TestEq (Mutant v t m a)

instance Constructible (t m a) => Constructible (Mutant v t m a) where
  type Repr (Mutant v t m a) = Repr (t m a)
  fromRepr = Mutant . fromRepr