-- 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.2.0 -- | 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 -- | Generalization of (.$) that does not require a Show -- instance at :: Predicate (x : xs) -> (Var, 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 -- | Variation on between for types that are Enum but not -- Integral -- -- This is useful for types such as Char. However, since this -- relies on Enum, it's limited by the precision of Int. enum :: Enum 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 :: forall a. (Ord a, Num a) => 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 => (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 in the specified range inRange :: Range a -> Gen a -- | Deprecated alias for inRange -- | Deprecated: Use inRange instead integral :: Range a -> Gen a -- | Deprecated alias for inRange -- | Deprecated: Use inRange instead enum :: Range a -> Gen a -- | Type-specialization of inRange int :: Range Int -> Gen Int -- | 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 a value with one of many generators -- -- Uniformly selects a generator and shrinks towards the first one. oneof :: NonEmpty (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 -- | This module defines something similar to QuickCheck's Arbitrary class -- along with some DerivingVia helpers. Our version, GenDefault, -- allows one to choose between sets of default generators with a -- user-defined tag. See Std for the standard tag with a few -- useful instances. module Test.Falsify.GenDefault class GenDefault tag a -- | Default generator for a -- -- The type-level tag allows types a to have multiple -- defaults. genDefault :: GenDefault tag a => Proxy tag -> Gen a -- | DerivingVia wrapper for types with default instances under other tags newtype ViaTag tag' a ViaTag :: a -> ViaTag tag' a [unViaTag] :: ViaTag tag' a -> a -- | DerivingVia wrapper for Integral types newtype ViaIntegral a ViaIntegral :: a -> ViaIntegral a [unViaIntegral] :: ViaIntegral a -> a -- | DerivingVia wrapper for Enum types newtype ViaEnum a ViaEnum :: a -> ViaEnum a [unViaEnum] :: ViaEnum a -> a -- | DerivingVia wrapper for FromList types newtype ViaList l (mn :: Nat) (mx :: Nat) ViaList :: l -> ViaList l (mn :: Nat) (mx :: Nat) [unViaList] :: ViaList l (mn :: Nat) (mx :: Nat) -> l -- | DerivingVia wrapper for FromString types newtype ViaString s (mn :: Nat) (mx :: Nat) ViaString :: s -> ViaString s (mn :: Nat) (mx :: Nat) [unViaString] :: ViaString s (mn :: Nat) (mx :: Nat) -> s -- | DerivingVia wrapper for Generic types newtype ViaGeneric tag a ViaGeneric :: a -> ViaGeneric tag a [unViaGeneric] :: ViaGeneric tag a -> a instance (GHC.Generics.Generic t, Test.Falsify.GenDefault.GGenDefault tag (GHC.Generics.Rep t)) => Test.Falsify.GenDefault.GenDefault tag (Test.Falsify.GenDefault.ViaGeneric tag t) instance Test.Falsify.GenDefault.GGenDefault tag GHC.Generics.U1 instance Test.Falsify.GenDefault.GGenDefault tag a => Test.Falsify.GenDefault.GGenDefault tag (GHC.Generics.M1 i c a) instance (Test.Falsify.GenDefault.GGenDefault tag a, Test.Falsify.GenDefault.GGenDefault tag b) => Test.Falsify.GenDefault.GGenDefault tag (a GHC.Generics.:*: b) instance (Test.Falsify.GenDefault.GGenDefault tag a, Test.Falsify.GenDefault.GGenDefault tag b) => Test.Falsify.GenDefault.GGenDefault tag (a GHC.Generics.:+: b) instance Test.Falsify.GenDefault.GenDefault tag a => Test.Falsify.GenDefault.GGenDefault tag (GHC.Generics.K1 i a) instance (Data.String.IsString s, Test.Falsify.GenDefault.GenDefault tag GHC.Types.Char, GHC.TypeNats.KnownNat mn, GHC.TypeNats.KnownNat mx) => Test.Falsify.GenDefault.GenDefault tag (Test.Falsify.GenDefault.ViaString s mn mx) instance (GHC.Exts.IsList l, Test.Falsify.GenDefault.GenDefault tag (GHC.Exts.Item l), GHC.TypeNats.KnownNat mn, GHC.TypeNats.KnownNat mx) => Test.Falsify.GenDefault.GenDefault tag (Test.Falsify.GenDefault.ViaList l mn mx) instance (GHC.Enum.Enum a, GHC.Enum.Bounded a) => Test.Falsify.GenDefault.GenDefault tag (Test.Falsify.GenDefault.ViaEnum a) instance (GHC.Real.Integral a, GHC.Bits.FiniteBits a, GHC.Enum.Bounded a) => Test.Falsify.GenDefault.GenDefault tag (Test.Falsify.GenDefault.ViaIntegral a) instance Test.Falsify.GenDefault.GenDefault tag' a => Test.Falsify.GenDefault.GenDefault tag (Test.Falsify.GenDefault.ViaTag tag' a) module Test.Falsify.GenDefault.Std -- | Type tag for these "standard" default generators. You can use this tag -- directly or choose type-by-type with ViaTag. data Std instance Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std () instance Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std GHC.Types.Bool instance Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std GHC.Types.Char instance Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std GHC.Types.Int instance Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std GHC.Int.Int8 instance Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std GHC.Int.Int16 instance Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std GHC.Int.Int32 instance Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std GHC.Int.Int64 instance Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std GHC.Types.Word instance Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std GHC.Word.Word8 instance Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std GHC.Word.Word16 instance Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std GHC.Word.Word32 instance Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std GHC.Word.Word64 instance Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std a => Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std (GHC.Maybe.Maybe a) instance (Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std a, Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std b) => Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std (Data.Either.Either a b) instance (Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std a, Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std b) => Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std (a, b) instance (Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std a, Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std b, Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std c) => Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std (a, b, c) instance (Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std a, Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std b, Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std c, Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std d) => Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std (a, b, c, d) instance (Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std a, Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std b, Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std c, Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std d, Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std e) => Test.Falsify.GenDefault.GenDefault Test.Falsify.GenDefault.Std.Std (a, b, c, d, e) -- | 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