{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE PartialTypeSignatures #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE ViewPatterns #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Test.Hspec.Hedgehog
(
hedgehog
, modifyArgs
, modifyMaxSuccess
, modifyMaxDiscardRatio
, modifyMaxSize
, modifyMaxShrinks
, module Hedgehog
) where
import Control.Monad.IO.Class (liftIO)
import Data.Coerce (coerce)
import Data.IORef (newIORef, readIORef, writeIORef)
import GHC.Stack (withFrozenCallStack)
import Hedgehog
import Hedgehog.Internal.Config (UseColor(..))
import Hedgehog.Internal.Property (DiscardLimit (..), Property (..),
PropertyConfig (..),
ShrinkLimit (..),
TerminationCriteria (..),
TestCount (..), TestLimit (..))
import Hedgehog.Internal.Report as Hedge
import Hedgehog.Internal.Runner (checkReport)
import qualified Hedgehog.Internal.Seed as Seed
import Hedgehog.Internal.Source (ColumnNo (..), LineNo (..),
Span (..))
import System.Random.SplitMix (unseedSMGen)
import Test.Hspec
import Test.Hspec.Core.Spec as Hspec
import Test.Hspec.QuickCheck (modifyArgs, modifyMaxDiscardRatio,
modifyMaxShrinks, modifyMaxSize,
modifyMaxSuccess)
import Test.QuickCheck.Random (QCGen (..))
import Test.QuickCheck.Test (Args (..))
hedgehog :: HasCallStack => PropertyT IO () -> PropertyT IO ()
hedgehog :: HasCallStack => PropertyT IO () -> PropertyT IO ()
hedgehog = forall a. a -> a
id
instance m ~ IO => Example (PropertyT m ()) where
type Arg (PropertyT m ()) = ()
evaluateExample :: PropertyT m ()
-> Params
-> (ActionWith (Arg (PropertyT m ())) -> IO ())
-> ProgressCallback
-> IO Result
evaluateExample PropertyT m ()
e = forall e.
Example e =>
e
-> Params
-> (ActionWith (Arg e) -> IO ())
-> ProgressCallback
-> IO Result
evaluateExample (\() -> PropertyT m ()
e)
propertyWithoutCallStack :: PropertyT IO () -> Property
propertyWithoutCallStack :: PropertyT IO () -> Property
propertyWithoutCallStack = forall a. HasCallStack => (HasCallStack => a) -> a
withFrozenCallStack HasCallStack => PropertyT IO () -> Property
property
instance (m ~ IO) => Example (a -> PropertyT m ()) where
type Arg (a -> PropertyT m ()) = a
evaluateExample :: (a -> PropertyT m ())
-> Params
-> (ActionWith (Arg (a -> PropertyT m ())) -> IO ())
-> ProgressCallback
-> IO Result
evaluateExample (forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap PropertyT IO () -> Property
propertyWithoutCallStack -> a -> Property
aprop) Params
params ActionWith (Arg (a -> PropertyT m ())) -> IO ()
aroundAction ProgressCallback
progressCallback = do
IORef Result
ref <- forall a. a -> IO (IORef a)
newIORef (String -> ResultStatus -> Result
Result String
"" (Maybe Location -> Maybe String -> ResultStatus
Pending forall a. Maybe a
Nothing forall a. Maybe a
Nothing))
ActionWith (Arg (a -> PropertyT m ())) -> IO ()
aroundAction forall a b. (a -> b) -> a -> b
$ \Arg (a -> PropertyT m ())
a -> do
let size :: Size
size = Size
0
prop :: Property
prop = a -> Property
aprop Arg (a -> PropertyT m ())
a
propConfig :: PropertyConfig
propConfig = PropertyConfig -> PropertyConfig
useQuickCheckArgs (Property -> PropertyConfig
propertyConfig Property
prop)
qcArgs :: Args
qcArgs = Params -> Args
paramsQuickCheckArgs Params
params
maxTests :: Int
maxTests = Args -> Int
maxSuccess Args
qcArgs
useQuickCheckArgs :: PropertyConfig -> PropertyConfig
useQuickCheckArgs PropertyConfig
pc =
PropertyConfig
pc
{ propertyTerminationCriteria :: TerminationCriteria
propertyTerminationCriteria =
case PropertyConfig -> TerminationCriteria
propertyTerminationCriteria PropertyConfig
pc of
EarlyTermination Confidence
x (TestLimit Int
_) ->
Confidence -> TestLimit -> TerminationCriteria
EarlyTermination Confidence
x (Int -> TestLimit
TestLimit Int
maxTests)
NoEarlyTermination Confidence
x (TestLimit Int
_) ->
Confidence -> TestLimit -> TerminationCriteria
NoEarlyTermination Confidence
x (Int -> TestLimit
TestLimit Int
maxTests)
NoConfidenceTermination (TestLimit Int
_) ->
TestLimit -> TerminationCriteria
NoConfidenceTermination (Int -> TestLimit
TestLimit Int
maxTests)
, propertyDiscardLimit :: DiscardLimit
propertyDiscardLimit =
Int -> DiscardLimit
DiscardLimit forall a b. (a -> b) -> a -> b
$ Args -> Int
maxDiscardRatio Args
qcArgs
, propertyShrinkLimit :: ShrinkLimit
propertyShrinkLimit =
Int -> ShrinkLimit
ShrinkLimit forall a b. (a -> b) -> a -> b
$ Args -> Int
maxShrinks Args
qcArgs
}
testCount :: Report a -> Int
testCount Report a
report =
case forall a. Report a -> TestCount
reportTests Report a
report of
TestCount Int
n -> Int
n
cb :: Report Progress -> IO ()
cb Report Progress
progress = do
case forall a. Report a -> a
reportStatus Report Progress
progress of
Progress
Running ->
ProgressCallback
progressCallback (forall {a}. Report a -> Int
testCount Report Progress
progress, Int
maxTests)
Shrinking FailureReport
_ ->
ProgressCallback
progressCallback (forall {a}. Report a -> Int
testCount Report Progress
progress, Int
maxTests)
Seed
seed <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ case Args -> Maybe (QCGen, Int)
replay (Params -> Args
paramsQuickCheckArgs Params
params) of
Maybe (QCGen, Int)
Nothing -> forall (m :: * -> *). MonadIO m => m Seed
Seed.random
Just (QCGen
rng, Int
_) -> forall (f :: * -> *) a. Applicative f => a -> f a
pure (forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry Word64 -> Word64 -> Seed
Seed (SMGen -> (Word64, Word64)
unseedSMGen (coerce :: forall a b. Coercible a b => a -> b
coerce QCGen
rng)))
Report Result
hedgeResult <- forall (m :: * -> *).
(MonadIO m, MonadCatch m) =>
PropertyConfig
-> Size
-> Seed
-> PropertyT m ()
-> (Report Progress -> m ())
-> m (Report Result)
checkReport PropertyConfig
propConfig Size
size Seed
seed (Property -> PropertyT IO ()
propertyTest Property
prop) Report Progress -> IO ()
cb
String
ppresult <- forall (m :: * -> *).
MonadIO m =>
UseColor -> Maybe PropertyName -> Report Result -> m String
renderResult UseColor
EnableColor forall a. Maybe a
Nothing Report Result
hedgeResult
forall a. IORef a -> a -> IO ()
writeIORef IORef Result
ref forall a b. (a -> b) -> a -> b
$ String -> ResultStatus -> Result
Result String
"" forall a b. (a -> b) -> a -> b
$ case forall a. Report a -> a
reportStatus Report Result
hedgeResult of
Failed FailureReport{String
[String]
[FailedAnnotation]
Maybe Diff
Maybe (Coverage CoverCount)
Maybe Span
ShrinkCount
ShrinkPath
failureShrinks :: FailureReport -> ShrinkCount
failureShrinkPath :: FailureReport -> ShrinkPath
failureCoverage :: FailureReport -> Maybe (Coverage CoverCount)
failureAnnotations :: FailureReport -> [FailedAnnotation]
failureLocation :: FailureReport -> Maybe Span
failureMessage :: FailureReport -> String
failureDiff :: FailureReport -> Maybe Diff
failureFootnotes :: FailureReport -> [String]
failureFootnotes :: [String]
failureDiff :: Maybe Diff
failureMessage :: String
failureLocation :: Maybe Span
failureAnnotations :: [FailedAnnotation]
failureCoverage :: Maybe (Coverage CoverCount)
failureShrinkPath :: ShrinkPath
failureShrinks :: ShrinkCount
..} ->
let
fromSpan :: Span -> Location
fromSpan Span{String
LineNo
ColumnNo
spanFile :: Span -> String
spanStartLine :: Span -> LineNo
spanStartColumn :: Span -> ColumnNo
spanEndLine :: Span -> LineNo
spanEndColumn :: Span -> ColumnNo
spanEndColumn :: ColumnNo
spanEndLine :: LineNo
spanStartColumn :: ColumnNo
spanStartLine :: LineNo
spanFile :: String
..} =
Location
{ locationFile :: String
locationFile = String
spanFile
, locationLine :: Int
locationLine = coerce :: forall a b. Coercible a b => a -> b
coerce LineNo
spanStartLine
, locationColumn :: Int
locationColumn = coerce :: forall a b. Coercible a b => a -> b
coerce ColumnNo
spanStartColumn
}
in
Maybe Location -> FailureReason -> ResultStatus
Hspec.Failure (Span -> Location
fromSpan forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Span
failureLocation) forall a b. (a -> b) -> a -> b
$ String -> FailureReason
ColorizedReason String
ppresult
Result
GaveUp ->
Maybe Location -> FailureReason -> ResultStatus
Failure forall a. Maybe a
Nothing (String -> FailureReason
Reason String
"GaveUp")
Result
OK ->
ResultStatus
Success
forall a. IORef a -> IO a
readIORef IORef Result
ref