falsify-0.1.1: Property-based testing with internal integrated shrinking

Test.Falsify.Generator

Description

Generator

Intended for qualified import.

import Test.Falsify.Generator (Gen)
import qualified Test.Falsify.Generator qualified as Gen
Synopsis

# Definition

data Gen a Source #

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

# Simple (non-compound) generators

Generate random bool, shrink towards the given value

Chooses with equal probability between True and False.

integral :: Integral a => Range a -> Gen a Source #

Generate value of integral type

Type-specialization of integral

enum :: forall a. Enum a => Range a -> Gen a Source #

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.

# Compound generators

## Taking advantage of Selective

choose :: Gen a -> Gen a -> Gen a Source #

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

### Binary trees

tree :: forall a. Range Word -> Gen a -> Gen (Tree a) Source #

Generate binary tree

bst :: forall a b. Integral a => (a -> Gen b) -> Interval a -> Gen (Tree (a, b)) Source #

Construct binary search tree

Shrinks by replacing entire subtrees by the empty tree.

### Shrink trees

data IsValidShrink p n Source #

Does a given shrunk value represent a valid shrink step?

Constructors

 ValidShrink p InvalidShrink n

#### Instances

Instances details
 (Show p, Show n) => Show (IsValidShrink p n) Source # Instance details MethodsshowsPrec :: Int -> IsValidShrink p n -> ShowS #show :: IsValidShrink p n -> String #showList :: [IsValidShrink p n] -> ShowS #

Arguments

 :: forall a p n. (a -> IsValidShrink p n) Predicate -> ShrinkTree a -> Gen (Either n (NonEmpty p))

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.

pathAny :: ShrinkTree a -> Gen (NonEmpty a) Source #

Variation on path without a predicate.

## Marking

data Marked f a Source #

Constructors

 Marked FieldsgetMark :: Mark unmark :: f a

#### Instances

Instances details
 Show (f a) => Show (Marked f a) Source # Instance detailsDefined in Data.Falsify.Marked MethodsshowsPrec :: Int -> Marked f a -> ShowS #show :: Marked f a -> String #showList :: [Marked f a] -> ShowS # Eq (f a) => Eq (Marked f a) Source # Instance detailsDefined in Data.Falsify.Marked Methods(==) :: Marked f a -> Marked f a -> Bool #(/=) :: Marked f a -> Marked f a -> Bool # Ord (f a) => Ord (Marked f a) Source # Instance detailsDefined in Data.Falsify.Marked Methodscompare :: Marked f a -> Marked f a -> Ordering #(<) :: Marked f a -> Marked f a -> Bool #(<=) :: Marked f a -> Marked f a -> Bool #(>) :: Marked f a -> Marked f a -> Bool #(>=) :: Marked f a -> Marked f a -> Bool #max :: Marked f a -> Marked f a -> Marked f a #min :: Marked f a -> Marked f a -> Marked f a #

data Mark Source #

Constructors

 Keep Drop

#### Instances

Instances details
 Source # Instance detailsDefined in Data.Falsify.Marked MethodsshowsPrec :: Int -> Mark -> ShowS #show :: Mark -> String #showList :: [Mark] -> ShowS # Source # Instance detailsDefined in Data.Falsify.Marked Methods(==) :: Mark -> Mark -> Bool #(/=) :: Mark -> Mark -> Bool # Source # Instance detailsDefined in Data.Falsify.Marked Methodscompare :: Mark -> Mark -> Ordering #(<) :: Mark -> Mark -> Bool #(<=) :: Mark -> Mark -> Bool #(>) :: Mark -> Mark -> Bool #(>=) :: Mark -> Mark -> Bool #max :: Mark -> Mark -> Mark #min :: Mark -> Mark -> Mark #

selectAllKept :: (Traversable t, Selective f) => t (Marked f a) -> f (t (Maybe a)) Source #

Traverse the argument, generating all values marked Keep, and replacing all values marked Drop by Nothing

mark :: Gen a -> Gen (Marked Gen a) Source #

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.

# Functions

## Generation

data Fun a b Source #

Function a -> b which can be shown, generated, and shrunk

#### Instances

Instances details
 Functor (Fun a) Source # Instance details Methodsfmap :: (a0 -> b) -> Fun a a0 -> Fun a b #(<$) :: a0 -> Fun a b -> Fun a a0 # (Show a, Show b) => Show (Fun a b) Source # Instance details MethodsshowsPrec :: Int -> Fun a b -> ShowS #show :: Fun a b -> String #showList :: [Fun a b] -> ShowS # applyFun :: Fun a b -> a -> b Source # Apply function to argument See also the Fn, Fn2, and Fn3 patter synonyms. pattern Fn :: (a -> b) -> Fun a b Source # Pattern synonym useful when generating functions of one argument pattern Fn2 :: (a -> b -> c) -> Fun (a, b) c Source # Pattern synonym useful when generating functions of two arguments pattern Fn3 :: (a -> b -> c -> d) -> Fun (a, b, c) d Source # Pattern synonym useful when generating functions of three arguments fun :: Function a => Gen b -> Gen (Fun a b) Source # Generate function a -> b given a generator for b ## Construction class Function a where Source # Generating functions Minimal complete definition Nothing Methods function :: Gen b -> Gen (a :-> b) Source # 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. default function :: (Generic a, GFunction (Rep a)) => Gen b -> Gen (a :-> b) Source # #### Instances Instances details  Source # Instance details Methodsfunction :: Gen b -> Gen (Int16 :-> b) Source # Source # Instance details Methodsfunction :: Gen b -> Gen (Int32 :-> b) Source # Source # Instance details Methodsfunction :: Gen b -> Gen (Int64 :-> b) Source # Source # Instance details Methodsfunction :: Gen b -> Gen (Int8 :-> b) Source # Source # Instance details Methodsfunction :: Gen b -> Gen (Word16 :-> b) Source # Source # Instance details Methodsfunction :: Gen b -> Gen (Word32 :-> b) Source # Source # Instance details Methodsfunction :: Gen b -> Gen (Word64 :-> b) Source # Source # Instance details Methodsfunction :: Gen b -> Gen (Word8 :-> b) Source # Source # Instance details Methodsfunction :: Gen b -> Gen (Integer :-> b) Source # Source # Instance details Methodsfunction :: Gen b -> Gen (Natural :-> b) Source # Function () Source # Instance details Methodsfunction :: Gen b -> Gen (() :-> b) Source # Source # Instance details Methodsfunction :: Gen b -> Gen (Bool :-> b) Source # Source # Instance details Methodsfunction :: Gen b -> Gen (Char :-> b) Source # Source # Instance details Methodsfunction :: Gen b -> Gen (Double :-> b) Source # Source # Instance details Methodsfunction :: Gen b -> Gen (Float :-> b) Source # Source # Instance details Methodsfunction :: Gen b -> Gen (Int :-> b) Source # Source # Instance details Methodsfunction :: Gen b -> Gen (Word :-> b) Source # (Integral a, Function a) => Function (Ratio a) Source # Instance details Methodsfunction :: Gen b -> Gen (Ratio a :-> b) Source # Function a => Function (Maybe a) Source # Instance details Methodsfunction :: Gen b -> Gen (Maybe a :-> b) Source # Function a => Function [a] Source # Instance details Methodsfunction :: Gen b -> Gen ([a] :-> b) Source # (Function a, Function b) => Function (Either a b) Source # Instance details Methodsfunction :: Gen b0 -> Gen (Either a b :-> b0) Source # (Function a, Function b) => Function (a, b) Source # Instance details Methodsfunction :: Gen b0 -> Gen ((a, b) :-> b0) Source # (Function a, Function b, Function c) => Function (a, b, c) Source # Instance details Methodsfunction :: Gen b0 -> Gen ((a, b, c) :-> b0) Source # (Function a, Function b, Function c, Function d) => Function (a, b, c, d) Source # Instance details Methodsfunction :: Gen b0 -> Gen ((a, b, c, d) :-> b0) Source # (Function a, Function b, Function c, Function d, Function e) => Function (a, b, c, d, e) Source # Instance details Methodsfunction :: Gen b0 -> Gen ((a, b, c, d, e) :-> b0) Source # (Function a, Function b, Function c, Function d, Function e, Function f) => Function (a, b, c, d, e, f) Source # Instance details Methodsfunction :: Gen b0 -> Gen ((a, b, c, d, e, f) :-> b0) Source # (Function a, Function b, Function c, Function d, Function e, Function f, Function g) => Function (a, b, c, d, e, f, g) Source # Instance details Methodsfunction :: Gen b0 -> Gen ((a, b, c, d, e, f, g) :-> b0) Source # data (:->) :: Type -> Type -> Type Source # #### Instances Instances details  Functor ((:->) a) Source # Instance details Methodsfmap :: (a0 -> b) -> (a :-> a0) -> a :-> b #(<$) :: a0 -> (a :-> b) -> a :-> a0 #

functionMap :: (b -> a) -> (a -> b) -> (a :-> c) -> b :-> c Source #

The basic building block for Function instances

Provides a Function instance by mapping to and from a type that already has a Function instance.

# Reducing precision

data WordN Source #

n-bit word

Constructors

 WordN Precision Word64

#### Instances

Instances details
 Source # Instance details MethodsshowsPrec :: Int -> WordN -> ShowS #show :: WordN -> String #showList :: [WordN] -> ShowS # Source # Instance details Methods(==) :: WordN -> WordN -> Bool #(/=) :: WordN -> WordN -> Bool # Source # Instance details Methods(<) :: WordN -> WordN -> Bool #(<=) :: WordN -> WordN -> Bool #(>) :: WordN -> WordN -> Bool #(>=) :: WordN -> WordN -> Bool #max :: WordN -> WordN -> WordN #min :: WordN -> WordN -> WordN #

Uniform selection of n-bit word of given precision, shrinking towards 0

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).

# Overriding shrinking

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.

shrinkToOneOf :: forall a. a -> [a] -> Gen a Source #

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.

firstThen :: forall a. a -> a -> Gen a Source #

Generator that always produces x as initial value, and shrinks to y

shrinkWith :: forall a. (a -> [a]) -> Gen a -> Gen a Source #

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.

shrinkToNothing :: Gen a -> Gen (Maybe a) Source #

Start with Just x for some x, then shrink to Nothing

# Shrink trees

fromShrinkTree :: forall a. Tree a -> Gen a Source #

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.

toShrinkTree :: forall a. Gen a -> Gen (Tree a) Source #

Expose the full shrink tree of a generator

This generator does not shrink.

# Generator independence

bindIntegral :: Integral a => Gen a -> (a -> Gen b) -> Gen b Source #

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.

perturb :: Integral a => a -> Gen b -> Gen b Source #

Run generator on different part of the sample tree depending on a

# Low-level

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.

primWith :: (Sample -> [Word64]) -> Gen Sample Source #

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.

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.

captureLocalTree :: Gen SampleTree Source #

Capture the local sample tree

This generator does not shrink.

bindWithoutShortcut :: Gen a -> (a -> Gen b) -> Gen b Source #

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.