packages feed

falsify-0.4.0: src/Test/Falsify/Interactive.hs

-- | Utilities for interaction with falsify in ghci
module Test.Falsify.Interactive (
    -- * Top-level driver
    falsify
  , falsify'
    -- * Investigating generators
  , sample
  , sampleUsing
  , shrink
  , shrink'
    -- * Re-exports
  , ReplaySeed(..)
  ) where

import Data.Default
import Data.List.NonEmpty (NonEmpty(..))
import System.Random.SplitMix

import Test.Falsify.Internal.Driver.ReplaySeed
import Test.Falsify.Internal.Generator
import Test.Falsify.Internal.Property

import qualified Test.Falsify.Driver     as Driver
import qualified Test.Falsify.SampleTree as SampleTree

{-------------------------------------------------------------------------------
  Top-level driver
-------------------------------------------------------------------------------}

-- | Try to falsify the given property
--
-- Reports the counter-example, if we find any.
falsify :: forall e a. Property' e a -> IO (Maybe e)
falsify = fmap (fmap snd . Driver.failure) . Driver.falsify def

-- | Generalization of 'falsify' that reports the full shrink history
falsify' :: forall e a. Property' e a -> IO (Maybe (NonEmpty e))
falsify' = fmap (fmap snd . Driver.failure') . Driver.falsify def

{-------------------------------------------------------------------------------
  Investigating generators
-------------------------------------------------------------------------------}

-- | Sample generator
sample :: Gen a -> IO a
sample g = (flip sampleUsing' g) <$> initSMGen

-- | Sample generator using the specified seed
sampleUsing :: ReplaySeed -> Gen a -> a
sampleUsing ReplaySeed{replaySeed, replayGamma} =
    sampleUsing' $ seedSMGen replaySeed replayGamma

-- | Internal generalization of 'sample' and 'sampleUsing'
sampleUsing' :: SMGen -> Gen a -> a
sampleUsing' prng g = fst $ runGen g (SampleTree.fromPRNG prng)

-- | Shrink counter-example
--
-- This will run the generator repeatedly until it finds a counter-example to
-- the given property, and will then shrink it.
--
-- Returns 'Nothing' if no counter-example could be found.
shrink :: forall a. (a -> Bool) -> Gen a -> IO (Maybe a)
shrink p g = falsify $ testGen' (\x -> aux x $ p x) g
  where
    aux :: a -> Bool -> Either a ()
    aux _ True  = Right ()
    aux x False = Left x

-- | Generalization of 'shrink'. Returns the full shrink history.
shrink' :: forall e a. (a -> Maybe e) -> Gen a -> IO (Maybe (NonEmpty e))
shrink' p g = falsify' $ testGen' (aux . p) g
  where
    aux :: Maybe e -> Either e ()
    aux Nothing  = Right ()
    aux (Just x) = Left x