{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Data.GenValidity.GenericSpec
  ( spec
  ) where

import GHC.Generics (Generic, Rep)

import Control.Monad

import Test.Hspec
import Test.QuickCheck

import Data.Proxy
import Data.Typeable

import Data.GenValidity

spec :: Spec
spec = do
  describe "genValidStructurally" $ do
    genValidstructurallySpec (Proxy :: Proxy Bool)
    genValidstructurallySpec (Proxy :: Proxy Ordering)
    genValidstructurallySpec (Proxy :: Proxy (Maybe Double))
    genValidstructurallySpec (Proxy :: Proxy (Either Double Ordering))
    genValidstructurallySpec (Proxy :: Proxy MyType)
  describe "shrinkValidStructurally" $ do
    shrinkValidstructurallySpec (Proxy :: Proxy Bool)
    shrinkValidstructurallySpec (Proxy :: Proxy Ordering)
    shrinkValidstructurallySpec (Proxy :: Proxy (Maybe Double))
    shrinkValidstructurallySpec (Proxy :: Proxy (Either Double Ordering))
    shrinkValidstructurallySpec (Proxy :: Proxy MyType)

genValidstructurallySpec ::
     forall a.
     (Validity a, Show a, Eq a, Typeable a, Generic a, GGenValid (Rep a))
  => Proxy a
  -> Spec
genValidstructurallySpec proxy =
  it (unwords ["only generates valid", "\"" ++ nameOf proxy ++ "\"s"]) $
  forAll (genValidStructurally :: Gen a) $ \a ->
    case prettyValidate a of
      Right _ -> return ()
      Left err ->
        expectationFailure $
        unlines
          [ "'validate' reported this value to be invalid: "
          , show a
          , "with explanation"
          , err
          , ""
          ]

shrinkValidstructurallySpec ::
     forall a.
     ( Validity a
     , Show a
     , Eq a
     , Typeable a
     , Generic a
     , GenValid a
     , GValidRecursivelyShrink (Rep a)
     , GValidSubterms (Rep a) a
     )
  => Proxy a
  -> Spec
shrinkValidstructurallySpec proxy = do
  it (unwords ["only shrinks to valid", "\"" ++ nameOf proxy ++ "\"s"]) $
    forAll (genValid :: Gen a) $ \a ->
      forM_ (shrinkValidStructurally a) $ \subA ->
        case prettyValidate subA of
          Right _ -> return ()
          Left err ->
            expectationFailure $
            unlines
              [ "'validate' reported this value to be invalid: "
              , show subA
              , "with explanation"
              , err
              , "but it should have been valid from shrinking"
              ]
  it
    (unwords
       ["never shrinks to itself for valid", "\"" ++ nameOf proxy ++ "\"s"]) $
    forAll (genValid :: Gen a) $ \a ->
      forM_ (shrinkValidStructurally a) $ \subA ->
        when (subA == a) $
        expectationFailure $ unlines [show a, "was shrunk to itself."]

nameOf ::
     forall a. Typeable a
  => Proxy a
  -> String
nameOf = show . typeRep

data MyType =
  MyType Double Ordering
  deriving (Show, Eq, Generic, Typeable)

instance Validity MyType

instance GenUnchecked MyType

instance GenValid MyType
