Safe Haskell | None |
---|---|

Language | Haskell2010 |

`GenValidity`

exists to make tests involving `Validity`

types easier and speed
up the generation of data for them.

Let's use the example from `Data.Validity`

again: A datatype that represents
primes.
To implement tests for this datatype, we would have to be able to generate
both primes and non-primes. We could do this with
`(Prime $ arbitrary) `

but this is tedious and inefficient.`suchThat`

isValid

The `GenValid`

type class allows you to specify how to (efficiently)
generate valid data of the given type to allow for easier and quicker testing.
Just instantiating `GenUnchecked`

already gives you access to a default instance
of `GenValid`

and `GenInvalid`

but writing custom implementations of these functions
may speed up the generation of data.

For example, to generate primes, we don't have to consider even numbers other than 2. A more efficient implementation could then look as follows:

instance GenUnchecked Prime where genUnchecked = Prime <$> arbitrary

instance GenValid Prime where genValid = Prime <$> (oneof [ pure 2 , ((\y -> 2 * abs y + 1) <$> arbitrary) `suchThat` isPrime) ])

Typical examples of tests involving validity could look as follows:

it "succeeds when given valid input" $ do forAllValid $ \input -> myFunction input `shouldSatisfy` isRight

it "produces valid output when it succeeds" $ do forAllUnchecked $ \input -> case myFunction input of Nothing -> return () -- Can happen Just output -> output `shouldSatisfy` isValid

Definitely also look at the genvalidity-property and genvalidity-hspec packages for more info on how to use this package.

## Synopsis

- class GenUnchecked a where
- genUnchecked :: Gen a
- shrinkUnchecked :: a -> [a]

- class Validity a => GenValid a where
- genValid :: Gen a
- shrinkValid :: a -> [a]

- class Validity a => GenInvalid a where
- genInvalid :: Gen a
- shrinkInvalid :: a -> [a]

- genValidStructurally :: (Validity a, Generic a, GGenValid (Rep a)) => Gen a
- genValidStructurallyWithoutExtraChecking :: (Generic a, GGenValid (Rep a)) => Gen a
- shrinkValidStructurally :: (Validity a, Generic a, GValidRecursivelyShrink (Rep a), GValidSubterms (Rep a) a) => a -> [a]
- shrinkValidStructurallyWithoutExtraFiltering :: (Generic a, GValidRecursivelyShrink (Rep a), GValidSubterms (Rep a) a) => a -> [a]
- upTo :: Int -> Gen Int
- genSplit :: Int -> Gen (Int, Int)
- genSplit3 :: Int -> Gen (Int, Int, Int)
- genSplit4 :: Int -> Gen (Int, Int, Int, Int)
- genSplit5 :: Int -> Gen (Int, Int, Int, Int, Int)
- arbPartition :: Int -> Gen [Int]
- shuffle :: [a] -> Gen [a]
- genListOf :: Gen a -> Gen [a]
- shrinkTuple :: (a -> [a]) -> (b -> [b]) -> (a, b) -> [(a, b)]
- shrinkT2 :: (a -> [a]) -> (a, a) -> [(a, a)]
- shrinkT3 :: (a -> [a]) -> (a, a, a) -> [(a, a, a)]
- shrinkT4 :: (a -> [a]) -> (a, a, a, a) -> [(a, a, a, a)]
- module Data.Validity
- genericGenUnchecked :: (Generic a, GGenUnchecked (Rep a)) => Gen a
- class GGenUnchecked f where
- gGenUnchecked :: Gen (f a)

- genericShrinkUnchecked :: (Generic a, GUncheckedRecursivelyShrink (Rep a), GUncheckedSubterms (Rep a) a) => a -> [a]
- uncheckedRecursivelyShrink :: (Generic a, GUncheckedRecursivelyShrink (Rep a)) => a -> [a]
- class GUncheckedRecursivelyShrink f where
- gUncheckedRecursivelyShrink :: f a -> [f a]

- uncheckedSubterms :: (Generic a, GUncheckedSubterms (Rep a) a) => a -> [a]
- class GUncheckedSubterms f a where
- gUncheckedSubterms :: f a -> [a]

- class GUncheckedSubtermsIncl f a where
- gUncheckedSubtermsIncl :: f a -> [a]

- class GGenValid f where
- class GValidRecursivelyShrink f where
- gValidRecursivelyShrink :: f a -> [f a]

- structurallyValidSubterms :: (Generic a, GValidSubterms (Rep a) a) => a -> [a]
- class GValidSubterms f a where
- gValidSubterms :: f a -> [a]

- class GValidSubtermsIncl f a where
- gValidSubtermsIncl :: f a -> [a]

# Documentation

class GenUnchecked a where Source #

A class of types for which truly arbitrary values can be generated.

### How to instantiate `GenUnchecked`

**Step 1**: Try to instantiate `GenUnchecked`

via `Generic`

.
**this is probably what you want**

An instance of this class can be made automatically if the type in question
has a `Generic`

instance. This instance will try to use `genUnchecked`

to
generate all structural sub-parts of the value that is being generated.

Example:

{-# LANGUAGE DeriveGeneric #-} data MyType = MyType Rational String deriving (Show, Eq, Generic) instance GenUnchecked MyType

generates something like:

instance GenUnchecked MyType where genUnchecked = MyType <$> genUnchecked <*> genUnchecked

If this is not possible because there is no `GenUnchecked`

instance available for one of the
sub-parts of your type, **then do not instantiate GenUnchecked for your type**.
Just continue with

`GenValid`

instead.**Step 2**: If an instatiation via `Generic`

is not possible, then you should emulate what
`genericGenUnchecked`

does.
This means that all sub-parts should be generated using `genUnchecked`

.
Make sure to generate any possible value, valid or not, that can exist at runtime
even when taking the existence of `unsafeCoerce`

into account.

### Warning: Invalid values can be funky

Some types have serious validity constraints. See `Rational`

for example.
These can behave very strangely when they are not valid.
In that case, **do not override GenUnchecked such that genUnchecked only generates valid values**.
In that case, do not override

`genUnchecked`

at all.
Instead, use `genValid`

from `GenValid`

(see below) instead and consider not instantiating `GenUnchecked`

at all.Nothing

genUnchecked :: Gen a Source #

genUnchecked :: (Generic a, GGenUnchecked (Rep a)) => Gen a Source #

shrinkUnchecked :: a -> [a] Source #

shrinkUnchecked :: (Generic a, GUncheckedRecursivelyShrink (Rep a), GUncheckedSubterms (Rep a) a) => a -> [a] Source #

## Instances

class Validity a => GenValid a where Source #

A class of types for which valid values can be generated.

### How to instantiate `GenValid`

**Step 1**: Try to instantiate `GenValid`

without overriding any functions.
This is only possible if your type has a `GenUnchecked`

instance.
If it doesn't, go to step 2.
It is possible that, if few values are valid or if validity
checking is expensive, that the resulting generator is too slow.
In that case, go to Step 2.

**Step 2**: Try to instantiate `GenValid`

using the helper functions via `Generic`

This involves using `genValidStructurally`

to override `genValid`

and
using `shrinkValidStructurally`

to override `shrinkValid`

.
**Every time you override genValid, you should also override shrinkValid**

**Step 3**: If the above is not possible due to lack of a `Generic`

instance,
then you should emulate what `genValidStructurally`

does.
This means that all sub-parts should be generated using `genValid`

.
Make sure to generate any possible valid value, but only valid values.

### A note about `Arbitrary`

If you also write `Arbitrary`

instances for `GenValid`

types, it may be
best to simply use

arbitrary = genValid shrink = shrinkValid

Nothing

Generate a valid datum, this should cover all possible valid values in the type

The default implementation is as follows:

genValid = genUnchecked `suchThat` isValid

To speed up testing, it may be a good idea to implement this yourself. If you do, make sure that it is possible to generate all possible valid data, otherwise your testing may not cover all cases.

genValid :: GenUnchecked a => Gen a Source #

Generate a valid datum, this should cover all possible valid values in the type

The default implementation is as follows:

genValid = genUnchecked `suchThat` isValid

To speed up testing, it may be a good idea to implement this yourself. If you do, make sure that it is possible to generate all possible valid data, otherwise your testing may not cover all cases.

shrinkValid :: a -> [a] Source #

Shrink a valid value.

The default implementation is as follows:

shrinkValid = filter isValid . shrinkUnchecked

It is important that this shrinking function only shrinks values to valid values.
If `shrinkValid`

ever shrinks a value to an invalid value, the test that is being shrunk for
might fail for a different reason than for the reason that it originally failed.
This would lead to very confusing error messages.

shrinkValid :: GenUnchecked a => a -> [a] Source #

Shrink a valid value.

The default implementation is as follows:

shrinkValid = filter isValid . shrinkUnchecked

It is important that this shrinking function only shrinks values to valid values.
If `shrinkValid`

ever shrinks a value to an invalid value, the test that is being shrunk for
might fail for a different reason than for the reason that it originally failed.
This would lead to very confusing error messages.

## Instances

class Validity a => GenInvalid a where Source #

A class of types for which invalid values can be generated.

### How to instantiate `GenInvalid`

**Step 1**: Realise that you probably do not want to.
It makes no sense, and serves no purpose, to instantiate `GenInvalid`

for types
which contain no invalid values. (In fact, the default implementation will go into
an infinite loop for such types.)
You should only instantiate `GenInvalid`

if you explicitly want to use it
to write tests that deal with invalid values, or if you are writing a container
for parametric values.

**Step 2**: Instantiate `GenInvalid`

without overriding any functions.

Nothing

genInvalid :: Gen a Source #

Generate an invalid datum, this should cover all possible invalid values

genInvalid = genUnchecked `suchThat` isInvalid

To speed up testing, it may be a good idea to implement this yourself. If you do, make sure that it is possible to generate all possible invalid data, otherwise your testing may not cover all cases.

genInvalid :: GenUnchecked a => Gen a Source #

Generate an invalid datum, this should cover all possible invalid values

genInvalid = genUnchecked `suchThat` isInvalid

To speed up testing, it may be a good idea to implement this yourself. If you do, make sure that it is possible to generate all possible invalid data, otherwise your testing may not cover all cases.

shrinkInvalid :: a -> [a] Source #

shrinkInvalid :: GenUnchecked a => a -> [a] Source #

## Instances

(GenUnchecked a, GenInvalid a) => GenInvalid [a] Source # | This instance ensures that the generated list contains at least one element
that satisfies |

Defined in Data.GenValidity genInvalid :: Gen [a] Source # shrinkInvalid :: [a] -> [[a]] Source # | |

GenInvalid a => GenInvalid (Maybe a) Source # | |

Defined in Data.GenValidity genInvalid :: Gen (Maybe a) Source # shrinkInvalid :: Maybe a -> [Maybe a] Source # | |

(Integral a, Num a, Ord a, Validity a, GenUnchecked a) => GenInvalid (Ratio a) Source # | |

Defined in Data.GenValidity genInvalid :: Gen (Ratio a) Source # shrinkInvalid :: Ratio a -> [Ratio a] Source # | |

(GenUnchecked a, GenInvalid a) => GenInvalid (NonEmpty a) Source # | |

Defined in Data.GenValidity genInvalid :: Gen (NonEmpty a) Source # shrinkInvalid :: NonEmpty a -> [NonEmpty a] Source # | |

(GenInvalid a, GenInvalid b) => GenInvalid (Either a b) Source # | This instance ensures that the generated tupse contains at least one invalid element. The other element is unchecked. |

Defined in Data.GenValidity genInvalid :: Gen (Either a b) Source # shrinkInvalid :: Either a b -> [Either a b] Source # | |

(GenUnchecked a, GenInvalid a, GenUnchecked b, GenInvalid b) => GenInvalid (a, b) Source # | |

Defined in Data.GenValidity genInvalid :: Gen (a, b) Source # shrinkInvalid :: (a, b) -> [(a, b)] Source # | |

(GenUnchecked a, GenUnchecked b, GenUnchecked c, GenInvalid a, GenInvalid b, GenInvalid c) => GenInvalid (a, b, c) Source # | This instance ensures that the generated triple contains at least one invalid element. The other two are unchecked. |

Defined in Data.GenValidity genInvalid :: Gen (a, b, c) Source # shrinkInvalid :: (a, b, c) -> [(a, b, c)] Source # | |

(GenUnchecked a, GenUnchecked b, GenUnchecked c, GenUnchecked d, GenInvalid a, GenInvalid b, GenInvalid c, GenInvalid d) => GenInvalid (a, b, c, d) Source # | This instance ensures that the generated triple contains at least one invalid element. The other two are unchecked. |

Defined in Data.GenValidity genInvalid :: Gen (a, b, c, d) Source # shrinkInvalid :: (a, b, c, d) -> [(a, b, c, d)] Source # | |

(GenUnchecked a, GenUnchecked b, GenUnchecked c, GenUnchecked d, GenUnchecked e, GenInvalid a, GenInvalid b, GenInvalid c, GenInvalid d, GenInvalid e) => GenInvalid (a, b, c, d, e) Source # | This instance ensures that the generated triple contains at least one invalid element. The other two are unchecked. |

Defined in Data.GenValidity genInvalid :: Gen (a, b, c, d, e) Source # shrinkInvalid :: (a, b, c, d, e) -> [(a, b, c, d, e)] Source # |

# Helper functions

genValidStructurally :: (Validity a, Generic a, GGenValid (Rep a)) => Gen a Source #

Generate a valid value by generating all the sub parts using the `Generic`

instance,
and trying that until a valid value has been generated

genValidStructurally = genValidStructurallyWithoutExtraChecking `suchThat` isValid

This is probably the function that you are looking for.
If you do use this function to override `genValid`

, you probably also want to use
`shrinkValidStructurally`

to override `shrinkValid`

.

genValidStructurallyWithoutExtraChecking :: (Generic a, GGenValid (Rep a)) => Gen a Source #

Generate a valid value by generating all the sub parts using the `Generic`

instance,

This generator is _not_ guaranteed to generate a valid value.

This is probably _not_ the function that you are looking for when overriding
`genValid`

_unless_ the type in question has no _extra_ validity constraints on top of
the validity of its sub parts.

shrinkValidStructurally :: (Validity a, Generic a, GValidRecursivelyShrink (Rep a), GValidSubterms (Rep a) a) => a -> [a] Source #

Shrink a term to any of its immediate valid subterms, and also recursively shrink all subterms, and then filtering out the results that are not valid.

shrinkValidStructurally = filter isValid . shrinkValidStructurallyWithoutExtraFiltering

This is probably the function that you are looking for.

shrinkValidStructurallyWithoutExtraFiltering :: (Generic a, GValidRecursivelyShrink (Rep a), GValidSubterms (Rep a) a) => a -> [a] Source #

Shrink a term to any of its immediate valid subterms, and also recursively shrink all subterms.

This shrinking function is _not_ guaranteed to shrink to valid values.

This is probably _not_ the function that you are looking for when overriding
`shrinkValid`

_unless_ the type in question has no _extra_ validity constraints on top of
the validity of its sub parts.

## Helper functions for implementing generators

genSplit :: Int -> Gen (Int, Int) Source #

'genSplit a' generates a tuple '(b, c)' such that 'b + c' equals `a`

.

genSplit3 :: Int -> Gen (Int, Int, Int) Source #

'genSplit3 a' generates a triple '(b, c, d)' such that 'b + c + d' equals `a`

.

genSplit4 :: Int -> Gen (Int, Int, Int, Int) Source #

'genSplit4 a' generates a quadruple '(b, c, d, e)' such that 'b + c + d + e' equals `a`

.

genSplit5 :: Int -> Gen (Int, Int, Int, Int, Int) Source #

'genSplit5 a' generates a quintuple '(b, c, d, e, f)' such that 'b + c + d + e + f' equals `a`

.

arbPartition :: Int -> Gen [Int] Source #

'arbPartition n' generates a list `ls`

such that 'sum ls' equals `n`

.

genListOf :: Gen a -> Gen [a] Source #

A version of `listOf`

that takes size into account more accurately.

This generator distributes the size that is is given among the values in the list that it generates.

## Helper functions for implementing shrinking functions

shrinkTuple :: (a -> [a]) -> (b -> [b]) -> (a, b) -> [(a, b)] Source #

shrinkT2 :: (a -> [a]) -> (a, a) -> [(a, a)] Source #

Turn a shrinking function into a function that shrinks tuples.

shrinkT3 :: (a -> [a]) -> (a, a, a) -> [(a, a, a)] Source #

Turn a shrinking function into a function that shrinks triples.

shrinkT4 :: (a -> [a]) -> (a, a, a, a) -> [(a, a, a, a)] Source #

Turn a shrinking function into a function that shrinks quadruples.

# Re-exports

module Data.Validity

# The Generics magic

genericGenUnchecked :: (Generic a, GGenUnchecked (Rep a)) => Gen a Source #

class GGenUnchecked f where Source #

gGenUnchecked :: Gen (f a) Source #

## Instances

GGenUnchecked (U1 :: Type -> Type) Source # | |

Defined in Data.GenValidity gGenUnchecked :: Gen (U1 a) Source # | |

GenUnchecked a => GGenUnchecked (K1 i a :: Type -> Type) Source # | |

Defined in Data.GenValidity gGenUnchecked :: Gen (K1 i a a0) Source # | |

(GGenUnchecked a, GGenUnchecked b) => GGenUnchecked (a :+: b) Source # | |

Defined in Data.GenValidity gGenUnchecked :: Gen ((a :+: b) a0) Source # | |

(GGenUnchecked a, GGenUnchecked b) => GGenUnchecked (a :*: b) Source # | |

Defined in Data.GenValidity gGenUnchecked :: Gen ((a :*: b) a0) Source # | |

GGenUnchecked a => GGenUnchecked (M1 i c a) Source # | |

Defined in Data.GenValidity gGenUnchecked :: Gen (M1 i c a a0) Source # |

genericShrinkUnchecked :: (Generic a, GUncheckedRecursivelyShrink (Rep a), GUncheckedSubterms (Rep a) a) => a -> [a] Source #

Shrink a term to any of its immediate subterms, and also recursively shrink all subterms.

uncheckedRecursivelyShrink :: (Generic a, GUncheckedRecursivelyShrink (Rep a)) => a -> [a] Source #

Recursively shrink all immediate uncheckedSubterms.

class GUncheckedRecursivelyShrink f where Source #

gUncheckedRecursivelyShrink :: f a -> [f a] Source #

## Instances

uncheckedSubterms :: (Generic a, GUncheckedSubterms (Rep a) a) => a -> [a] Source #

All immediate uncheckedSubterms of a term.

class GUncheckedSubterms f a where Source #

gUncheckedSubterms :: f a -> [a] Source #

## Instances

GUncheckedSubterms (V1 :: Type -> Type) a Source # | |

Defined in Data.GenValidity gUncheckedSubterms :: V1 a -> [a] Source # | |

GUncheckedSubterms (U1 :: Type -> Type) a Source # | |

Defined in Data.GenValidity gUncheckedSubterms :: U1 a -> [a] Source # | |

GUncheckedSubterms (K1 i a :: Type -> Type) b Source # | |

Defined in Data.GenValidity gUncheckedSubterms :: K1 i a b -> [b] Source # | |

(GUncheckedSubtermsIncl f a, GUncheckedSubtermsIncl g a) => GUncheckedSubterms (f :+: g) a Source # | |

Defined in Data.GenValidity gUncheckedSubterms :: (f :+: g) a -> [a] Source # | |

(GUncheckedSubtermsIncl f a, GUncheckedSubtermsIncl g a) => GUncheckedSubterms (f :*: g) a Source # | |

Defined in Data.GenValidity gUncheckedSubterms :: (f :*: g) a -> [a] Source # | |

GUncheckedSubterms f a => GUncheckedSubterms (M1 i c f) a Source # | |

Defined in Data.GenValidity gUncheckedSubterms :: M1 i c f a -> [a] Source # |

class GUncheckedSubtermsIncl f a where Source #

gUncheckedSubtermsIncl :: f a -> [a] Source #

## Instances

class GValidRecursivelyShrink f where Source #

gValidRecursivelyShrink :: f a -> [f a] Source #

## Instances

GValidRecursivelyShrink (V1 :: Type -> Type) Source # | |

Defined in Data.GenValidity gValidRecursivelyShrink :: V1 a -> [V1 a] Source # | |

GValidRecursivelyShrink (U1 :: Type -> Type) Source # | |

Defined in Data.GenValidity gValidRecursivelyShrink :: U1 a -> [U1 a] Source # | |

GenValid a => GValidRecursivelyShrink (K1 i a :: Type -> Type) Source # | |

Defined in Data.GenValidity gValidRecursivelyShrink :: K1 i a a0 -> [K1 i a a0] Source # | |

(GValidRecursivelyShrink f, GValidRecursivelyShrink g) => GValidRecursivelyShrink (f :+: g) Source # | |

Defined in Data.GenValidity gValidRecursivelyShrink :: (f :+: g) a -> [(f :+: g) a] Source # | |

(GValidRecursivelyShrink f, GValidRecursivelyShrink g) => GValidRecursivelyShrink (f :*: g) Source # | |

Defined in Data.GenValidity gValidRecursivelyShrink :: (f :*: g) a -> [(f :*: g) a] Source # | |

GValidRecursivelyShrink f => GValidRecursivelyShrink (M1 i c f) Source # | |

Defined in Data.GenValidity gValidRecursivelyShrink :: M1 i c f a -> [M1 i c f a] Source # |

structurallyValidSubterms :: (Generic a, GValidSubterms (Rep a) a) => a -> [a] Source #

All immediate validSubterms of a term.

class GValidSubterms f a where Source #

gValidSubterms :: f a -> [a] Source #

## Instances

GValidSubterms (V1 :: Type -> Type) a Source # | |

Defined in Data.GenValidity gValidSubterms :: V1 a -> [a] Source # | |

GValidSubterms (U1 :: Type -> Type) a Source # | |

Defined in Data.GenValidity gValidSubterms :: U1 a -> [a] Source # | |

GValidSubterms (K1 i a :: Type -> Type) b Source # | |

Defined in Data.GenValidity gValidSubterms :: K1 i a b -> [b] Source # | |

(GValidSubtermsIncl f a, GValidSubtermsIncl g a) => GValidSubterms (f :+: g) a Source # | |

Defined in Data.GenValidity gValidSubterms :: (f :+: g) a -> [a] Source # | |

(GValidSubtermsIncl f a, GValidSubtermsIncl g a) => GValidSubterms (f :*: g) a Source # | |

Defined in Data.GenValidity gValidSubterms :: (f :*: g) a -> [a] Source # | |

GValidSubterms f a => GValidSubterms (M1 i c f) a Source # | |

Defined in Data.GenValidity gValidSubterms :: M1 i c f a -> [a] Source # |

class GValidSubtermsIncl f a where Source #

gValidSubtermsIncl :: f a -> [a] Source #

## Instances

GValidSubtermsIncl (V1 :: Type -> Type) a Source # | |

Defined in Data.GenValidity gValidSubtermsIncl :: V1 a -> [a] Source # | |

GValidSubtermsIncl (U1 :: Type -> Type) a Source # | |

Defined in Data.GenValidity gValidSubtermsIncl :: U1 a -> [a] Source # | |

GValidSubtermsIncl (K1 i a :: Type -> Type) b Source # | |

Defined in Data.GenValidity gValidSubtermsIncl :: K1 i a b -> [b] Source # | |

GValidSubtermsIncl (K1 i a :: Type -> Type) a Source # | |

Defined in Data.GenValidity gValidSubtermsIncl :: K1 i a a -> [a] Source # | |

(GValidSubtermsIncl f a, GValidSubtermsIncl g a) => GValidSubtermsIncl (f :+: g) a Source # | |

Defined in Data.GenValidity gValidSubtermsIncl :: (f :+: g) a -> [a] Source # | |

(GValidSubtermsIncl f a, GValidSubtermsIncl g a) => GValidSubtermsIncl (f :*: g) a Source # | |

Defined in Data.GenValidity gValidSubtermsIncl :: (f :*: g) a -> [a] Source # | |

GValidSubtermsIncl f a => GValidSubtermsIncl (M1 i c f) a Source # | |

Defined in Data.GenValidity gValidSubtermsIncl :: M1 i c f a -> [a] Source # |