-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Property-based testing with internal integrated shrinking -- -- This library provides property based testing with support for internal -- integrated shrinking: integrated in the sense of Hedgehog, meaning -- that there is no need to write a separate shrinker and generator; and -- internal in the sense of Hypothesis, meaning that this works well even -- across monadic bind. However, the actual techniques that power -- falsify are quite different from both of these two libraries. -- -- Most users will probably want to use the integration with -- tasty, and use Test.Tasty.Falsify as their main -- entrypoint into the library. The Test.Falsify.Interactive -- module can be used to experiment with the library in ghci. @package falsify @version 0.1.1 -- | Predicates -- -- Intended for qualified import. module Test.Falsify.Predicate -- | N-ary predicate -- -- A predicate of type -- --
--   Predicate '[Int, Bool, Char, ..]
--   
-- -- is essentially a function Int -> Bool -> Char -> .. -> -- Bool, along with some metadata about that function that allows us -- to render it in a human readable way. In particular, we construct an -- Expr for the values that the predicate has been applied to. data Predicate :: [Type] -> Type -- | Simple expression language -- -- The internal details of this type are (currently) not exposed. data Expr prettyExpr :: Expr -> String -- | Function (used for composition of a Predicate with a function) data Fn a b -- | Default constructor for a function fn :: Show b => (Var, a -> b) -> Fn a b -- | Generalization of fn that does not depend on Show fnWith :: (Var, b -> String, a -> b) -> Fn a b -- | Function that should not be visible in any rendered failure -- -- Consider these two predicates: -- --
--   p1, p2 :: Predicate '[Char, Char]
--   p1 = P.eq `P.on` (P.fn "ord"    ord)
--   p2 = P.eq `P.on` (P.transparent ord)
--   
-- -- Both of these compare two characters on their codepoints (through -- ord), but they result in different failures. The first would -- give us something like -- --
--   (ord x) /= (ord y)
--   x    : 'a'
--   y    : 'b'
--   ord x: 97
--   ord y: 98
--   
-- -- whereas the second might give us something like -- --
--   x /= y
--   x: 'a'
--   y: 'b'
--   
-- -- which of these is more useful is of course application dependent. transparent :: (a -> b) -> Fn a b -- | Constant True alwaysPass :: Predicate xs -- | Constant False alwaysFail :: Predicate xs -- | Unary predicate -- -- This is essentially a function a -> Bool; see -- Predicate for detailed discussion. unary :: (a -> Bool) -> (Expr -> Err) -> Predicate '[a] -- | Binary predicate -- -- This is essentially a function a -> b -> Bool; see -- Predicate for detailed discussion. binary :: (a -> b -> Bool) -> (Expr -> Expr -> Err) -> Predicate [a, b] -- | Specialization of unary for unary relations satisfies :: (Var, a -> Bool) -> Predicate '[a] -- | Specialization of binary for relations relatedBy :: (Var, a -> b -> Bool) -> Predicate [a, b] -- | Function composition (analogue of (.)) dot :: Predicate (x : xs) -> Fn y x -> Predicate (y : xs) -- | Analogue of 'Control.Arrow.(***)' split :: Predicate (x' : (y' : xs)) -> (Fn x x', Fn y y') -> Predicate (x : (y : xs)) -- | Analogue of on on :: Predicate (x : (x : xs)) -> Fn y x -> Predicate (y : (y : xs)) -- | Analogue of flip flip :: Predicate (x : (y : zs)) -> Predicate (y : (x : zs)) -- | Match on the argument, and apply whichever predicate is applicable. matchEither :: Predicate (a : xs) -> Predicate (b : xs) -> Predicate (Either a b : xs) -- | Conditional -- -- This is a variation on choose that provides no evidence for -- which branch is taken. matchBool :: Predicate xs -> Predicate xs -> Predicate (Bool : xs) -- | Evaluate fully applied predicate eval :: Predicate '[] -> Either Err () -- | Infix version of at -- -- Typical usage example: -- --
--   assert $
--        P.relatedBy ("equiv", equiv)
--     .$ ("x", x)
--     .$ ("y", y)
--   
(.$) :: Show x => Predicate (x : xs) -> (Var, x) -> Predicate xs -- | Generation of (.$) that does not require a Show instance at :: Predicate (x : xs) -> (Expr, String, x) -> Predicate xs -- | Equal eq :: Eq a => Predicate [a, a] -- | Not equal ne :: Eq a => Predicate [a, a] -- | (Strictly) less than lt :: Ord a => Predicate [a, a] -- | Less than or equal to le :: Ord a => Predicate [a, a] -- | (Strictly) greater than gt :: Ord a => Predicate [a, a] -- | Greater than or equal to ge :: Ord a => Predicate [a, a] -- | Check that values get closed to the specified target towards :: forall a. (Show a, Ord a, Num a) => a -> Predicate [a, a] -- | Specialization of eq, useful when expecting a specific value in -- a test expect :: (Show a, Eq a) => a -> Predicate '[a] -- | Check that lo <= x <= hi between :: (Show a, Ord a) => a -> a -> Predicate '[a] -- | Number is even even :: Integral a => Predicate '[a] -- | Number is odd odd :: Integral a => Predicate '[a] -- | Membership check elem :: Eq a => Predicate [[a], a] -- | Apply predicate to every pair of consecutive elements in the list pairwise :: forall a. Show a => Predicate [a, a] -> Predicate '[[a]] instance GHC.Base.Monoid (Test.Falsify.Predicate.Predicate a) instance GHC.Base.Semigroup (Test.Falsify.Predicate.Predicate a) -- | Numerical ranges module Test.Falsify.Range -- | Range of values data Range a -- | Uniform selection between the given bounds, shrinking towards first -- bound between :: forall a. (Integral a, FiniteBits a) => (a, a) -> Range a -- | Selection within the given bounds, shrinking towards the specified -- origin -- -- All else being equal, prefers values in the second half of the -- range (in the common case of say withOrigin (-100, 100) 0, -- this means we prefer positive values). withOrigin :: (Integral a, FiniteBits a) => (a, a) -> a -> Range a -- | Introduce skew (non-uniform selection) -- -- A skew of s == 0 means no skew: uniform selection. -- -- A positive skew (s > 0) introduces a bias towards smaller -- values (this is the typical use case). As example, for a skew of s -- == 1: -- -- -- -- A negative skew (s < 0) introduces a bias towards larger -- values. For a skew of s == 1: -- -- -- -- The table below lists values for the percentage of the range used, -- given a percentage of the time (a value of 0 means a single value from -- the range): -- --
--      | time%
--    s | 50% | 90%
--   --------------
--    0 |  50 |  90
--    1 |  13 |  56
--    2 |   4 |  35
--    3 |   1 |  23
--    4 |   0 |  16
--    5 |   0 |  11
--    6 |   0 |   8
--    7 |   0 |   6
--    8 |   0 |   5
--    9 |   0 |   4
--   10 |   0 |   3
--   
-- -- Will shrink towards x, independent of skew. -- -- NOTE: The implementation currently uses something similar to μ-law -- encoding. As a consequence, the generator gets increased precision -- near the end of the range we skew towards, and less precision near the -- other end. This means that not all values in the range can be -- produced. skewedBy :: forall a. (FiniteBits a, Integral a) => Double -> (a, a) -> Range a -- | Origin of the range (value we shrink towards) origin :: Range a -> a -- | Value x such that 0 <= x < 1 data ProperFraction pattern ProperFraction :: Double -> ProperFraction -- | Precision (in bits) newtype Precision Precision :: Word8 -> Precision -- | Range that is x everywhere constant :: a -> Range a -- | Construct a given a fraction -- -- Precondition: f must be monotonically increasing or -- decreasing; i.e. -- -- fromProperFraction :: Precision -> (ProperFraction -> a) -> Range a -- | Generate value in any of the specified ranges, then choose the one -- that is closest to the specified origin -- -- Precondition: the target must be within the bounds of all ranges. towards :: a -> [Range a] -> Range a -- | Evaluate a range, given an action to generate fractions -- -- Most users will probably never need to call this function. eval :: forall f a. (Applicative f, Ord a, Num a) => (Precision -> f ProperFraction) -> Range a -> f a -- | Generator -- -- Intended for qualified import. -- --
--   import Test.Falsify.Generator (Gen)
--   import qualified Test.Falsify.Generator qualified as Gen
--   
module Test.Falsify.Generator -- | Generator of a random value -- -- Generators can be combined through their Functor, -- Applicative and Monad interfaces. The primitive -- generator is prim, but most users will probably want to -- construct their generators using the predefined from -- Test.Falsify.Generator as building blocks. -- -- Generators support "internal integrated shrinking". Shrinking is -- integrated in the sense of Hedgehog, meaning that we don't -- write a separate shrinker at all, but the shrink behaviour is implied -- by the generator. For example, if you have a generator -- genList for a list of numbers, then -- --
--   filter even <$> genList
--   
-- -- will only generate even numbers, and that property is automatically -- preserved during shrinking. Shrinking is internal in the sense -- of Hypothesis, meaning that unlike in Hedgehog, shrinking works -- correctly even in the context of monadic bind. For example, if you do -- --
--   do n <- genListLength
--      replicateM n someOtherGen
--   
-- -- then we can shrink n and the results from -- someOtherGen in any order (that said, users may prefer to use -- the dedicated list generator for this purpose, which improves -- on this in a few ways). -- -- NOTE: Gen is NOT an instance of Alternative; -- this would not be compatible with the generation of infinite data -- structures. For the same reason, we do not have a monad transformer -- version of Gen either. data Gen a -- | Generate random bool, shrink towards the given value -- -- Chooses with equal probability between True and False. bool :: Bool -> Gen Bool -- | Generate value of integral type integral :: Integral a => Range a -> Gen a -- | Type-specialization of integral int :: Range Int -> Gen Int -- | Generate value of enumerable type -- -- For most types integral is preferred; the Enum class -- goes through Int, and is therefore also limited by the range of -- Int. enum :: forall a. Enum a => Range a -> Gen a -- | Generate a value with one of two generators -- -- Shrinks towards the first generator;the two generators can shrink -- independently from each other. -- --

Background

-- -- In the remainder of this docstring we give some background to this -- function, which may be useful for general understanding of the -- falsify library. -- -- The implementation takes advantage of the that Gen is a -- selective functor to ensure that the two generators can shrink -- independently: if the initial value of the generator is some -- y produced by the second generator, later shrunk to some -- y', then if the generator can shrink to x at some -- point, produced by the first generator, then shrinking -- effectively "starts over": the value of x is independent of -- y'. -- -- That is different from doing this: -- --
--   do b <- bool
--      if b then l else r
--   
-- -- In this case, l and r will be generated from the -- same sample tree, and so cannot shrink independently. -- -- It is also different from -- --
--   do x <- l
--      y <- r
--      b <- bool
--      return $ if b then x else y
--   
-- -- In this case, l and r are run against -- different sample trees, like we do here, but in this -- case if the current value produced by the generator is produced by the -- right generator, then the sample tree used for the left generator will -- always shrink to Minimal (this must be possible -- because we're not currently using it); this means that we would then -- only be able to shrink to a value from the left generator if the -- minimal value produced by that generator happens to work. -- -- To rephrase that last point: generating values that are not actually -- used will lead to poor shrinking, since those values can always be -- shrunk to their minimal value, independently from whatever property is -- being tested: the shrinker does not know that the value is not being -- used. The correct way to conditionally use a value is to use the -- selective interface, as we do here. choose :: Gen a -> Gen a -> Gen a -- | Generate list of specified length -- -- Shrinking behaviour: -- -- -- -- Note on shrinking predictability: in the case that the specified -- Range has an origin which is neither the lower bound nor the -- upper bound (and only in that case), list can have confusing -- shrinking behaviour. For example, suppose we have a range (0, -- 10) with origin 5. Then we could start by generating an -- intermediate list of length of 10 and then subsequently drop 5 -- elements from that, resulting in an optimal list length. However, we -- can now shrink that length from 10 to 2 (which is closer to 5, after -- all), but now we only have 2 elements to work with, and hence the -- generated list will now drop from 5 elements to 2. This is not -- necessarily a problem, because that length 2 can now subsequently -- shrink further towards closer to the origin (5), but nonetheless it -- might result in confusing intermediate shrinking steps. list :: Range Word -> Gen a -> Gen [a] -- | Choose random element -- -- Shrinks towards earlier elements. -- -- NOTE: Does not work on infinite lists (it computes the length of the -- list). elem :: NonEmpty a -> Gen a -- | Generalization of elem that additionally returns the parts of -- the list before and after the element pick :: NonEmpty a -> Gen ([a], a, [a]) -- | Choose random element from a list -- -- This is different from elem: it avoids first computing the -- length of the list, and is biased towards elements earlier in the -- list. The advantage is that this works for infinite lists, too. -- -- Also returns the elements from the list before and after the chosen -- element. pickBiased :: NonEmpty a -> Gen ([a], a, [a]) -- | Shuffle list (construct a permutation) -- -- Shrinking behaviour: shuffle is defined in terms of -- permutation, which provides some guarantees: it shrinks towards -- making changes near the start of the list, and towards swapping -- fewer elements of the list. -- -- It is difficult to define precisely how this affects the resulting -- list, but we can say that if for a particular counter-example -- it suffices if two lists are different in one element, then the -- shuffled list will in fact only be different in one place from -- the original, and that one element will have been swapped with an -- immediate neighbour. shuffle :: [a] -> Gen [a] -- | Permutation is a sequence of swaps type Permutation = [(Word, Word)] applyPermutation :: Permutation -> [a] -> [a] -- | Generate permutation for a list of length n -- -- This is essentially an implemention of Fisher-Yates, in that we -- generate a series of swaps (i, j), with 1 <= i <= n - 1 and -- 0 <= j <= i, except that -- -- -- -- This ensures that we shrink towards making swaps nearer the -- start of the list, as well as towards fewer swaps. -- -- We make no attempt to make the permutation canonical; doing so makes -- it extremely difficult to get predicable shrinking behaviour. permutation :: Word -> Gen Permutation -- | Choose generator with the given frequency -- -- For example, -- --
--   frequency [
--       (1, genA)
--     , (2, genB)
--     ]
--   
-- -- will use genA 13rd of the time, and genB -- 23rds. -- -- Shrinks towards generators earlier in the list; the generators -- themselves are independent from each other (shrinking of genB -- does not affect shrinking of genA). -- -- Precondition: there should at least one generator with non-zero -- frequency. frequency :: forall a. [(Word, Gen a)] -> Gen a data Tree a Leaf :: Tree a pattern Branch :: a -> Tree a -> Tree a -> Tree a drawTree :: Tree String -> String -- | Generate binary tree tree :: forall a. Range Word -> Gen a -> Gen (Tree a) -- | Construct binary search tree -- -- Shrinks by replacing entire subtrees by the empty tree. bst :: forall a b. Integral a => (a -> Gen b) -> Interval a -> Gen (Tree (a, b)) type ShrinkTree = Tree -- | Does a given shrunk value represent a valid shrink step? data IsValidShrink p n ValidShrink :: p -> IsValidShrink p n InvalidShrink :: n -> IsValidShrink p n -- | Generate semi-random path through the tree -- -- Will only construct paths that satisfy the given predicate (typically, -- a property that is being tested). -- -- Shrinks towards shorter paths, and towards paths that use subtrees -- that appear earlier in the list of subtrees at any node in the tree. -- -- See also pathAny. path :: forall a p n. (a -> IsValidShrink p n) -> ShrinkTree a -> Gen (Either n (NonEmpty p)) -- | Variation on path without a predicate. pathAny :: ShrinkTree a -> Gen (NonEmpty a) data Marked f a Marked :: Mark -> f a -> Marked f a [getMark] :: Marked f a -> Mark [unmark] :: Marked f a -> f a data Mark Keep :: Mark Drop :: Mark -- | Traverse the argument, generating all values marked Keep, and -- replacing all values marked Drop by Nothing selectAllKept :: (Traversable t, Selective f) => t (Marked f a) -> f (t (Maybe a)) -- | Mark an element, shrinking towards Drop -- -- This is similar to shrinkToNothing, except that Marked -- still has a value in the Drop case: marks are merely hints, -- that we may or may not use. mark :: Gen a -> Gen (Marked Gen a) -- | Function a -> b which can be shown, generated, and shrunk data Fun a b -- | Apply function to argument -- -- See also the Fn, Fn2, and Fn3 patter synonyms. applyFun :: Fun a b -> a -> b -- | Pattern synonym useful when generating functions of one argument pattern Fn :: (a -> b) -> Fun a b -- | Pattern synonym useful when generating functions of two arguments pattern Fn2 :: (a -> b -> c) -> Fun (a, b) c -- | Pattern synonym useful when generating functions of three arguments pattern Fn3 :: (a -> b -> c -> d) -> Fun (a, b, c) d -- | Generate function a -> b given a generator for b fun :: Function a => Gen b -> Gen (Fun a b) -- | Generating functions class Function a -- | Build reified function -- -- (:->) is an abstract type; if you need to add additional -- Function instances, you need to use functionMap, or rely -- on the default implementation in terms of generics. function :: Function a => Gen b -> Gen (a :-> b) -- | Build reified function -- -- (:->) is an abstract type; if you need to add additional -- Function instances, you need to use functionMap, or rely -- on the default implementation in terms of generics. function :: (Function a, Generic a, GFunction (Rep a)) => Gen b -> Gen (a :-> b) data (:->) :: Type -> Type -> Type -- | The basic building block for Function instances -- -- Provides a Function instance by mapping to and from a type that -- already has a Function instance. functionMap :: (b -> a) -> (a -> b) -> (a :-> c) -> b :-> c -- | n-bit word data WordN WordN :: Precision -> Word64 -> WordN -- | Uniform selection of n-bit word of given precision, shrinking -- towards 0 wordN :: Precision -> Gen WordN -- | Uniform selection of fraction, shrinking towards 0 -- -- Precondition: precision must be at least 1 bit (a zero-bit number is -- constant 0; it is meaningless to have a fraction in a point range). properFraction :: HasCallStack => Precision -> Gen ProperFraction -- | Disable shrinking in the given generator -- -- Due to the nature of internal shrinking, it is always possible that a -- generator gets reapplied to samples that were shrunk wrt to a -- different generator. In this sense, withoutShrinking -- should be considered to be a hint only. -- -- This function is only occassionally necessary; most users will -- probably not need to use it. withoutShrinking :: Gen a -> Gen a -- | Start with x, then shrink to one of the xs -- -- Once shrunk, will not shrink again. -- -- Minimal value is the first shrunk value, if it exists, and the -- original otherwise. shrinkToOneOf :: forall a. a -> [a] -> Gen a -- | Generator that always produces x as initial value, and -- shrinks to y firstThen :: forall a. a -> a -> Gen a -- | Shrink with provided shrinker -- -- This provides compatibility with QuickCheck-style manual shrinking. -- -- Defined in terms of fromShrinkTree; see discussion there for -- some notes on performance. shrinkWith :: forall a. (a -> [a]) -> Gen a -> Gen a -- | Start with Just x for some x, then shrink to -- Nothing shrinkToNothing :: Gen a -> Gen (Maybe a) -- | Construct generator from shrink tree -- -- This provides compatibility with Hedgehog-style integrated shrinking. -- -- This is O(n^2) in the number of shrink steps: as this shrinks, the -- generator is growing a path of indices which locates a particular -- value in the shrink tree (resulting from unfolding the provided -- shrinking function). At each step during the shrinking process the -- shrink tree is re-evaluated and the next value in the tree is located; -- since this path throws linearly, the overall cost is O(n^2). -- -- The O(n^2) cost is only incurred on locating the next element -- to be tested; the property is not reevaluated at already-shrunk -- values. fromShrinkTree :: forall a. Tree a -> Gen a -- | Expose the full shrink tree of a generator -- -- This generator does not shrink. toShrinkTree :: forall a. Gen a -> Gen (Tree a) -- | Selective bind -- -- Unlike monadic bind, the RHS is generated and shrunk completely -- independently for each different value of a produced by the -- LHS. -- -- This is a generalization of bindS to arbitrary integral values; -- it is also much more efficient than bindS. -- -- NOTE: This is only one way to make a generator independent. See -- perturb for more primitive combinator. bindIntegral :: Integral a => Gen a -> (a -> Gen b) -> Gen b -- | Run generator on different part of the sample tree depending on -- a perturb :: Integral a => a -> Gen b -> Gen b -- | Uniform selection of Word64, shrinking towards 0, using binary -- search -- -- This is a primitive generator; most users will probably not want to -- use this generator directly. prim :: Gen Word64 -- | Generalization of prim that allows to override the shrink -- behaviour -- -- This is only required in rare circumstances. Most users will probably -- never need to use this generator. primWith :: (Sample -> [Word64]) -> Gen Sample -- | Generate arbitrary value x <= n -- -- Unlike prim, exhaustive does not execute binary search. -- Instead, all smaller values are considered. This is potentially -- very expensive; the primary use case for this generator is testing -- shrinking behaviour, where binary search can lead to some -- unpredicatable results. -- -- This does NOT do uniform selection: for small n, the -- generator will with overwhelming probability produce n itself -- as initial value. -- -- This is a primitive generator; most users will probably not want to -- use this generator directly. exhaustive :: Word64 -> Gen Word64 -- | Capture the local sample tree -- -- This generator does not shrink. captureLocalTree :: Gen SampleTree -- | Varation on (>>=) that doesn't apply the shortcut to -- Minimal -- -- This function is primarily useful for debugging falsify -- itself; users will probably never need it. bindWithoutShortcut :: Gen a -> (a -> Gen b) -> Gen b -- | Properties -- -- Intended for unqualified import. -- -- Most users will probably use Test.Tasty.Falsify instead of this -- module. module Test.Falsify.Property -- | Property -- -- A Property is a generator that can fail and keeps a track of -- some information about the test run. -- -- In most cases, you will probably want to use Property instead, -- which fixes e at String. data Property' e a -- | Property that uses strings as errors type Property = Property' String -- | Generate value and add it to the log gen :: (HasCallStack, Show a) => Gen a -> Property' e a -- | Generalization of gen that doesn't depend on a Show -- instance -- -- No log entry is added if Nothing. genWith :: HasCallStack => (a -> Maybe String) -> Gen a -> Property' e a -- | Test failure testFailed :: e -> Property' e a -- | Fail the test if the predicate does not hold assert :: Predicate '[] -> Property' String () -- | Log some additional information about the test -- -- This will be shown in verbose mode. info :: String -> Property' e () -- | Discard this test discard :: Property' e a -- | Variation on collect that does not rely on Show -- -- See collect for detailed discussion. label :: String -> [String] -> Property' e () -- | Label this test -- -- See also label, which does not rely on Show. -- --

Motivation

-- -- Labelling is instrumental in understanding the distribution of test -- data. For example, consider testing a binary tree type, and we want to -- test some properties of an insert operation (example from -- "How to specify it!" by John Hughes): -- --
--   prop_insert_insert :: Property ()
--   prop_insert_insert = do
--     tree     <- gen $ ..
--     (k1, v1) <- gen $ ..
--     (k2, v2) <- gen $ ..
--     assert $ .. (insert k1 v1 $ insert k2 v2 $ tree) ..
--   
-- -- We might want to know in what percentage of tests k1 == k2: -- --
--   collect "sameKey" [k1 == k2]
--   
-- -- When we do, falsify will report in which percentage of tests -- the key are the same, and in which percentage of tests they are not. -- --

Labels with multiple values

-- -- In general, a particular label can have multiple values in any given -- test run. Given a test of n test runs, for each value -- v reported, falsify will report what percentage of -- the n runs are labelled with v. That means that -- these percentages may not add up to 100%; indeed, if we had -- --
--   collect "sameKey" [True]
--   ..
--   collect "sameKey" [False]
--   
-- -- or, equivalently, -- --
--   collect "sameKey" [True, False]
--   
-- -- then every test would have been reported as labelled with -- True (100%) as well as with False@ (also -- 100%). Of course, if we do (like above) -- --
--   collect "sameKey" [k1 == k2]
--   
-- -- each test will be labelled with either True or -- False, and the percentages will add up to 100%. -- --

Difference from QuickCheck

-- -- Since you can call collect anywhere in a property, it is -- natural that the same label can have multiple values in any -- given test run. In this regard, collect is closer to -- QuickCheck's tabulate. However, the statistics of -- tabulate can be difficult to interpret; QuickCheck reports -- the frequency of a value as a percentage of the total number of -- values collected; the frequency reported by falsify here -- is always in terms of number of test runs, like collect does -- in QuickCheck. We therefore opted to use the name collect -- rather than tabulate. collect :: Show a => String -> [a] -> Property' e () -- | Test shrinking of a property -- -- A property is normally only shrunk when it fails. We do the -- same here: if the property succeeds, we discard the test and try -- again. -- -- If the given property itself discards immediately, then this generator -- will discard also; otherwise, only shrink steps are considered that do -- not lead to a discard. testShrinking :: forall e. Show e => Predicate [e, e] -> Property' e () -> Property' String () -- | Test the minimum error thrown by the property -- -- If the given property passes, we will discard this test (in that case, -- there is nothing to test); this test is also discarded if the given -- property discards. -- -- NOTE: When testing a particular generator, you might still want to -- test with some particular property in mind. Otherwise, the minimum -- value will always simply be the value that the generator produces when -- given the Minimal sample tree. testMinimum :: forall e. Show e => Predicate '[e] -> Property' e () -> Property' String () -- | Test output of the generator testGen :: forall a. Show a => Predicate '[a] -> Gen a -> Property' String () -- | Test shrinking of a generator -- -- We check any shrink step that the generator can make -- (independent of any property). testShrinkingOfGen :: Show a => Predicate [a, a] -> Gen a -> Property' String () -- | Utilities for interaction with falsify in ghci module Test.Falsify.Interactive -- | Try to falsify the given property -- -- Reports the counter-example, if we find any. falsify :: forall e a. Property' e a -> IO (Maybe e) -- | Generalization of falsify that reports the full shrink history falsify' :: forall e a. Property' e a -> IO (Maybe (NonEmpty e)) -- | Sample generator sample :: Gen a -> IO a -- | 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) -- | Generalization of shrink. Returns the full shrink history. shrink' :: forall e a. (a -> Maybe e) -> Gen a -> IO (Maybe (NonEmpty e)) -- | Pattern synonym useful when generating functions of one argument pattern Fn :: (a -> b) -> Fun a b -- | Pattern synonym useful when generating functions of two arguments pattern Fn2 :: (a -> b -> c) -> Fun (a, b) c -- | Pattern synonym useful when generating functions of three arguments pattern Fn3 :: (a -> b -> c -> d) -> Fun (a, b, c) d -- | Support for falsify in the tasty framework -- -- As is customary, this also re-exports parts of the falsify -- API, but not modules such as Test.Falsify.Range that are -- intended to be imported qualified. module Test.Tasty.Falsify -- | Generalization of testPropertyWith using default options testProperty :: TestName -> Property' String () -> TestTree data TestOptions TestOptions :: ExpectFailure -> Maybe Verbose -> Maybe Word -> Maybe Word -> Maybe Word -> TestOptions -- | Do we expect this test to fail? [expectFailure] :: TestOptions -> ExpectFailure -- | Override verbose mode for this test [overrideVerbose] :: TestOptions -> Maybe Verbose -- | Override the maximum number of shrink steps for this test [overrideMaxShrinks] :: TestOptions -> Maybe Word -- | Override the number of tests [overrideNumTests] :: TestOptions -> Maybe Word -- | Override how many tests can be discarded per successful test [overrideMaxRatio] :: TestOptions -> Maybe Word -- | Verbose output -- -- Note that if a test fails (and we were not expecting failure) we show -- the logs independent of verbosity. data Verbose Verbose :: Verbose NotVerbose :: Verbose -- | Do we expect the property to fail? -- -- If ExpectFailure, the test will fail if the property does -- not fail. Note that if we expect failure for a property, then -- we can stop at the first failed test; the number of tests to run for -- the property becomes a maximum rather than a goal. data ExpectFailure ExpectFailure :: ExpectFailure DontExpectFailure :: ExpectFailure testPropertyWith :: TestOptions -> TestName -> Property' String () -> TestTree -- | Generator of a random value -- -- Generators can be combined through their Functor, -- Applicative and Monad interfaces. The primitive -- generator is prim, but most users will probably want to -- construct their generators using the predefined from -- Test.Falsify.Generator as building blocks. -- -- Generators support "internal integrated shrinking". Shrinking is -- integrated in the sense of Hedgehog, meaning that we don't -- write a separate shrinker at all, but the shrink behaviour is implied -- by the generator. For example, if you have a generator -- genList for a list of numbers, then -- --
--   filter even <$> genList
--   
-- -- will only generate even numbers, and that property is automatically -- preserved during shrinking. Shrinking is internal in the sense -- of Hypothesis, meaning that unlike in Hedgehog, shrinking works -- correctly even in the context of monadic bind. For example, if you do -- --
--   do n <- genListLength
--      replicateM n someOtherGen
--   
-- -- then we can shrink n and the results from -- someOtherGen in any order (that said, users may prefer to use -- the dedicated list generator for this purpose, which improves -- on this in a few ways). -- -- NOTE: Gen is NOT an instance of Alternative; -- this would not be compatible with the generation of infinite data -- structures. For the same reason, we do not have a monad transformer -- version of Gen either. data Gen a -- | Pattern synonym useful when generating functions of one argument pattern Fn :: (a -> b) -> Fun a b -- | Pattern synonym useful when generating functions of two arguments pattern Fn2 :: (a -> b -> c) -> Fun (a, b) c -- | Pattern synonym useful when generating functions of three arguments pattern Fn3 :: (a -> b -> c -> d) -> Fun (a, b, c) d