hedgehog-classes-0.1.2: Hedgehog will eat your typeclass bugs

Safe HaskellNone
LanguageHaskell2010

Hedgehog.Classes

Contents

Description

This library provides sets of properties that should hold for common typeclasses.

Note: functions that test laws of a subclass never test the laws of a superclass. For example, commutativeSemigroupLaws never tests the laws provided by semigroupLaws.

Synopsis

Running

lawsCheck Source #

Arguments

:: Laws

The Laws you would like to check.

-> IO Bool

True if your tests pass, False otherwise.

A convenience function for testing the properties of a typeclass. For example, in GHCi:

>>> genOrdering :: Gen Ordering; genOrdering = frequency [(1,pure EQ),(1,pure LT),(1,pure GT)]
>>> lawsCheck (monoidLaws genOrdering)
  ✓ <interactive> passed 100 tests.
  ✓ <interactive> passed 100 tests.
  ✓ <interactive> passed 100 tests.
  ✓ <interactive> passed 100 tests.
True

lawsCheckOne Source #

Arguments

:: Gen a

The generator for your type.

-> [Gen a -> Laws]

Functions that take a generator and output Laws.

-> IO Bool

True if your tests pass. False otherwise.

A convenience function for testing many typeclass instances of a single type.

>>> lawsCheckOne (word8 constantBounded) [jsonLaws, showReadLaws]
  ✓ <interactive> passed 100 tests.
  ✓ <interactive> passed 100 tests.
  ✓ <interactive> passed 100 tests.
  ✓ <interactive> passed 100 tests.
  ✓ <interactive> passed 100 tests.
  ✓ <interactive> passed 100 tests.
  ✓ <interactive> passed 100 tests.
True

lawsCheckMany Source #

Arguments

:: [(String, [Laws])]

Pairs of type names and their associated laws to test.

-> IO Bool

True if your tests pass. False otherwise.

A convenience function for checking many typeclass instances of multiple types.

import Control.Applicative (liftA2)

import Data.Map (Map)
import Data.Set (Set)

import qualified Data.List as List
import qualified Data.Set as Set
import qualified Data.Map as Map

import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range

import Hedgehog (Gen)
import Hedgehog.Classes

-- Generate a small Set Int
genSet :: Gen (Set Int)
genSet = Set.fromList $ (Gen.list (Range.linear 2 10) (Gen.int Range.constantBounded))

-- Generate a small Map String Int
genMap :: Gen (Map String Int)
genMap = Map.fromList $ (liftA2 List.zip genStrings genInts)
  where
    rng = Range.linear 2 6
    genStrings = Gen.list rng (Gen.string rng Gen.lower)
    genInts = Gen.list rng (Gen.int Range.constantBounded)

commonLaws :: (Eq a, Monoid a, Show a) => Gen a -> [Laws]
commonLaws p = [eqLaws p, monoidLaws p]

tests :: [(String, [Laws])]
tests =
  [ ("Set Int", commonLaws genSet)
  , ("Map String Int", commonLaws genMap)
  ]

Now, in GHCi:

>>> lawsCheckMany tests
Testing properties for common typeclasses...

-------------
-- Set Int --
-------------

  ✓ interactive passed 100 tests.
  ✓ interactive passed 100 tests.
  ✓ interactive passed 100 tests.
  ✓ interactive passed 100 tests.
  ✓ interactive passed 100 tests.
  ✓ interactive passed 100 tests.
  ✓ interactive passed 100 tests.

--------------------
-- Map String Int --
--------------------

  ✓ interactive passed 100 tests.
  ✓ interactive passed 100 tests.
  ✓ interactive passed 100 tests.
  ✓ interactive passed 100 tests.
  ✓ interactive passed 100 tests.
  ✓ interactive passed 100 tests.
  ✓ interactive passed 100 tests.


All tests succeeded
True

Properties

Ground types

bitsLaws :: (FiniteBits a, Show a) => Gen a -> Laws Source #

Tests the following Bits laws:

Conjunction Idempotence
n .&. nn
Disjunction Idempotence
n .|. nn
Double Complement
complement . complementid
Set Bit
setBit n i ≡ n .|. bit i
Clear Bit
clearBit n in .&. complement (bit i)
Complement Bit
complement n ixor n (bit i)
Clear Zero
clearBit zeroBits izeroBits
Set Zero
setBit zeroBits izeroBits
Test Zero
testBit zeroBits iFalse
Pop Zero
popCount zeroBits0
Count Leading Zeros of Zero
countLeadingZeros zeroBitsfiniteBitSize (undefined :: a)
Count Trailing Zeros of Zero
countTrailingZeros zeroBitsfiniteBitSize (undefined :: a)

eqLaws :: (Eq a, Show a) => Gen a -> Laws Source #

Tests the following Eq laws:

Reflexivity
x == xTrue
Symmetry
x == yy == x
Transitivity
x == y && y == zx == z
Negation
x /= ynot (x == y)

integralLaws :: (Integral a, Show a) => Gen a -> Laws Source #

Tests the following Integral laws:

Quotient Remainder
quot x y * y + (rem x y)x
Division Modulus
(div x y) * y + (mod x y)x
Integer Roundtrip
fromInteger . toIntegerid

monoidLaws :: (Eq a, Monoid a, Show a) => Gen a -> Laws Source #

Tests the following Monoid laws:

Left Identity
mappend memptyid
Right Identity
flip mappend memptyid
Associativity
mappend a (mappend b c)mappend (mappend a b) c
Concatenation
mconcatfoldr mappend mempty

commutativeMonoidLaws :: (Eq a, Monoid a, Show a) => Gen a -> Laws Source #

Tests the following Monoid laws:

Commutativity
mappend a bmappend b a

ordLaws :: forall a. (Ord a, Show a) => Gen a -> Laws Source #

Tests the following Ord laws:

Antisymmetry
x <= y && y <= xx == y
Transitivity
x <= y && y <= zx <= z
Reflexivity
x <= xTrue
Totality
x <= y || y <= xTrue

enumLaws :: (Enum a, Eq a, Show a) => Gen a -> Laws Source #

Tests the following Enum laws:

Succ-Pred Identity
succ . predid
Pred-Succ Identity
pred . succid

boundedEnumLaws :: (Bounded a, Enum a, Eq a, Show a) => Gen a -> Laws Source #

Tests the same laws as enumLaws, but uses the Bounded constraint to ensure that succ and pred behave as though they are total. This should always be preferred if your type has a Bounded instance.

semigroupLaws :: (Eq a, Semigroup a, Show a) => Gen a -> Laws Source #

Tests the following Semigroup laws:

Associativity
a <> (b <> c)(a <> b) <> c
Concatenation
sconcatfoldr1 (<>)
Times
stimes n afoldr1 (<>) (replicate n a)

commutativeSemigroupLaws :: (Eq a, Semigroup a, Show a) => Gen a -> Laws Source #

Tests the following Semigroup laws:

Commutativity
a <> bb <> a

exponentialSemigroupLaws :: (Eq a, Semigroup a, Show a) => Gen a -> Laws Source #

Tests the following Semigroup laws:

Exponentiality
stimes n (a <> b)stimes n a <> stimes n b

idempotentSemigroupLaws :: (Eq a, Semigroup a, Show a) => Gen a -> Laws Source #

Tests the following Semigroup laws:

Idempotency
a <> aa

rectangularBandSemigroupLaws :: (Eq a, Semigroup a, Show a) => Gen a -> Laws Source #

Tests the following Semigroup laws:

Rectangular Bandedness
a <> b <> aa

jsonLaws :: (FromJSON a, ToJSON a, Eq a, Show a) => Gen a -> Laws Source #

Tests the following ToJSON / FromJSON laws:

Encoding Partial Isomorphism
decode . encodeJust
Encoding Equals Value
decode . encodeJust . toJSON

genericLaws :: (Generic a, Eq a, Show a, Eq (Rep a x), Show (Rep a x)) => Gen a -> Gen (Rep a x) -> Laws Source #

Tests the following Generic laws:

From-To Inverse
from . toid
To-From Inverse
to . fromid

semiringLaws :: (Semiring a, Eq a, Show a) => Gen a -> Laws Source #

Tests the following Semiring laws:

Additive Left Identity
zero + xx
Additive Right Identity
x + zerox
Additive Associativity
x + (y + z)(x + y) + z
Additive Commutativity
x + yy + x
Multiplicative Left Identity
one * xx
Multiplicative Right Identity
x * onex
Multiplicative Associativity
x * (y * z)(x * y) * z
Multiplicatiion Left-Distributes Over Addtion
x * (y + z)(x * y) + (x * z)
Multiplication Right-Distibutes Over Addition
(y + z) * x(y * x) + (z * x)
Multiplicative Left Annihilation
zero * xzero
Multiplicative Right Annihilation
x * zerozero

ringLaws :: (Ring a, Eq a, Show a) => Gen a -> Laws Source #

Tests the following Ring laws:

Additive Inverse
negate x + xzero

starLaws :: (Star a, Eq a, Show a) => Gen a -> Laws Source #

Tests the following Star laws:

Asteration
star xone + x * star x
APlus
aplus xx * star x

showLaws :: Show a => Gen a -> Laws Source #

Tests the following Show laws:

ShowsPrec Zero
show ashowsPrec 0 a ""
ShowsPrec Equivariance
showsPrec p a r ++ s'showsPrec p a (r ++ s)
ShowsPrec ShowList
showList as r ++ sshowList as (r ++ s)

showReadLaws :: (Eq a, Read a, Show a) => Gen a -> Laws Source #

Tests the following Show / Read laws:

Partial Isomorphism: show/read
readMaybe . showJust
Partial Isomorphism: show/read with initial space
readMaybe . (" " ++) . showJust
Partial Isomorphism: showsPrec/readPrec
(a,"") elem readsPrec p (showsPrec p a "")True
Partial Isomorphism: showList/readList
(as,"") elem readList (showList as "")True
Partial Isomorphism: showListWith shows/readListDefault
(as,"") elem readListDefault (showListWith shows as "")True

storableLaws :: (Eq a, Show a, Storable a) => Gen a -> Laws Source #

Tests the following Storable laws:

Set-Get
pokeElemOff ptr ix a >> peekElemOff ptr ixpure a
Get-Set
peekElemOff ptr ix >>= pokeElemOff ptr ixpure () (Putting back what you got out has no effect)
List Conversion Roundtrips
Mallocing a list and then reconstructing it gives you the same list
PeekElemOff/Peek
peekElemOff a ipeek (plusPtr a (i * sizeOf undefined))
PokeElemOff/Poke
pokeElemOff a i xpoke (plusPtr a (i * sizeOf undefined)) x
PeekByteOff/Peek
peekByteOff a ipeek (plusPtr a i)
PokeByteOff/Peek
pokeByteOff a i xpoke (plusPtr a i) x

Unary type constructors

alternativeLaws :: (Alternative f, forall x. Eq x => Eq (f x), forall x. Show x => Show (f x)) => (forall x. Gen x -> Gen (f x)) -> Laws Source #

Tests the following Alternative laws:

Left Identity
empty <|> aa
Right Identity
a <|> emptya
Associativity
a <|> (b <|> c)(a <|> b) <|> c

applicativeLaws :: (Applicative f, forall x. Eq x => Eq (f x), forall x. Show x => Show (f x)) => (forall x. Gen x -> Gen (f x)) -> Laws Source #

Tests the following Applicative laws:

Identity
pure id <*> vv
Composition
pure (.) <*> u <*> v <*> wu <*> (v <*> w)
Homomorphism
pure f <*> pure x ≡ pure (f x)
Interchange
u <*> pure ypure ($ y) <*> u
LiftA2 1
liftA2 id f xf <*> x
LiftA2 2
liftA2 f x yf <$> x <*> y

contravariantLaws :: (Contravariant f, forall x. Eq x => Eq (f x), forall x. Show x => Show (f x)) => (forall x. Gen x -> Gen (f x)) -> Laws Source #

Tests the following Contravariant laws:

Identity
contramap idid
Composition
contramap f . contramap gcontramap (g . f)

foldableLaws :: (Foldable f, forall x. Eq x => Eq (f x), forall x. Show x => Show (f x)) => (forall x. Gen x -> Gen (f x)) -> Laws Source #

Tests the following Foldable laws:

Fold
foldfoldMap id
FoldMap
foldMap f ≡ foldr (mappend . f) mempty
Foldr
foldr f z t ≡ appEndo (foldMap (Endo . f) t) z
Foldr'
foldr' f z0 t ≡ foldl f' id t z0, where f' k x z = k $! f x z
Foldl
foldl f z t ≡ appEndo (getDual (foldMap (Dual . Endo . flip f) t)) z
Foldl'
foldl' f z0 xs ≡ foldr f' id xs z0, where f' x k z = k $! f z x
Foldl1
foldl1 f t ≡ let (x:xs) = toList t in foldl f x xs
Foldr1
foldr1 f t ≡ let (xs,x) = unsnoc (toList t) in foldr f x xs
ToList
toListfoldr (:) []
Null
nullfoldr (const (const False)) True
Length
lengthgetSum . foldMap (const (Sum 1))

This additionally tests that the user's implementations of foldr' and foldl' are strict in their accumulators.

functorLaws :: (Functor f, forall x. Eq x => Eq (f x), forall x. Show x => Show (f x)) => (forall x. Gen x -> Gen (f x)) -> Laws Source #

Tests the following Functor laws:

Identity
fmap idid
Composition
fmap f . fmap gfmap (f . g)
Const
fmap (const x)x <$

monadLaws :: (Monad f, forall x. Eq x => Eq (f x), forall x. Show x => Show (f x)) => (forall x. Gen x -> Gen (f x)) -> Laws Source #

Tests the following Monad laws:

Left Identity
return a >>= kk a
Right Identity
m >>= returnm
Associativity
m >>= (\x -> k x >>= h)(m >>= k) >>= h
Return
returnpure
Ap
ap f xf <*> x

monadIOLaws :: (MonadIO f, forall x. Eq x => Eq (f x), forall x. Show x => Show (f x)) => (forall x. Gen x -> Gen (f x)) -> Laws Source #

Tests the following MonadIO laws:

Return
liftIO . returnreturn
Lift
liftIO (m >>= f)liftIO m >>= (liftIO . f)

monadPlusLaws :: (MonadPlus f, forall x. Eq x => Eq (f x), forall x. Show x => Show (f x)) => (forall x. Gen x -> Gen (f x)) -> Laws Source #

Tests the following MonadPlus laws:

Left Identity
mplus mzeroid
Right Identity
flip mplus mzeroid
Associativity
mplus a (mplus b c)mplus (mplus a b) c
Left Zero
mzero >>= fmzero
Right Zero
v >> mzeromzero

monadZipLaws :: (MonadZip f, forall x. Eq x => Eq (f x), forall x. Show x => Show (f x)) => (forall x. Gen x -> Gen (f x)) -> Laws Source #

Tests the following MonadZip laws:

Naturality
fmap (f *** g) (mzip ma mb)mzip (fmap f ma) (fmap g mb)

traversableLaws :: (Traversable f, forall x. Eq x => Eq (f x), forall x. Show x => Show (f x)) => (forall x. Gen x -> Gen (f x)) -> Laws Source #

Tests the following Traversable laws:

Naturality
t . traverse ftraverse (t . f), for every applicative transformation t
Identity
traverse IdentityIdentity
Composition
traverse (Compose . fmap g . f)Compose . fmap (traverse g) . traverse f
SequenceA Naturality
t . sequenceAsequenceA . fmap t, for every applicative transformation t
SequenceA Identity
sequenceA . fmap IdentityIdentity
SequenceA Composition
sequenceA . fmap ComposeCompose . fmap sequenceA . sequenceA
FoldMap
foldMapfoldMapDefault
Fmap
fmapfmapDefault

Binary type constructors

arrowLaws :: forall f. (Arrow f, forall x y. (Eq x, Eq y) => Eq (f x y), forall x y. (Show x, Show y) => Show (f x y)) => (forall x y. Gen x -> Gen y -> Gen (f x y)) -> Laws Source #

Tests the following Arrow laws:

Arr Identity
arr idid
Arr Composition
arr (f >>> g)arr f >>> arr g
Arr-First inverse
first (arr f)arr (first f)
First Composition
first (f >>> g)first f >>> first g
Arrow Law 5
first f >>> arr fstarr fst >>> f
Arrow Law 6
first f >>> arr (id *** g)arr (id *** g) >>> first f
Arrow Law 7
first (first f) >>> arr assocarr assoc >>> first f, where assoc ((a,b),c) = (a,(b,c))

bifoldableLaws :: forall f. (Bifoldable f, forall x y. (Eq x, Eq y) => Eq (f x y), forall x y. (Show x, Show y) => Show (f x y)) => (forall x y. Gen x -> Gen y -> Gen (f x y)) -> Laws Source #

Tests the following Bifoldable laws:

Identity
bifoldbifoldMap id id
FoldMap
bifoldMap f gbifoldr (mappend . f) (mappend . g) mempty
Foldr
bifoldr f g z tappEndo (bifoldMap (Endo . f) (Endo . g) t) z

bifoldableFunctorLaws :: forall f. (Bifoldable f, Bifunctor f, forall x y. (Eq x, Eq y) => Eq (f x y), forall x y. (Show x, Show y) => Show (f x y)) => (forall x y. Gen x -> Gen y -> Gen (f x y)) -> Laws Source #

Tests the following Bifoldable / Bifunctor laws:

Composition
bifoldMap f gbifold . bimap f g
FoldMap
bifoldMap f g . bimap h ibifoldMap (f . h) (g . i)

bifunctorLaws :: forall f. (Bifunctor f, forall x y. (Eq x, Eq y) => Eq (f x y), forall x y. (Show x, Show y) => Show (f x y)) => (forall x y. Gen x -> Gen y -> Gen (f x y)) -> Laws Source #

Tests the following Bifunctor laws:

Identity
bimap id idid
First Identity
first idid
Second Identity
second idid
Composition
bimap id idfirst id . second id

bitraversableLaws :: forall f. (Bitraversable f, forall x y. (Eq x, Eq y) => Eq (f x y), forall x y. (Show x, Show y) => Show (f x y)) => (forall x y. Gen x -> Gen y -> Gen (f x y)) -> Laws Source #

Tests the following Bitraversable laws:

Naturality
bitraverse (t . f) (t . g)t . bitraverse f g, for every applicative transformation t
Identity
bitraverse Identity IdentityIdentity
Composition
Compose . fmap (bitraverse g1 g2) . bitraverse f1 f2bitraverse (Compose . fmap g1 . f1) (Compose . fmap g2 . f2)

categoryLaws :: forall f. (Category f, forall x y. (Eq x, Eq y) => Eq (f x y), forall x y. (Show x, Show y) => Show (f x y)) => (forall x y. Gen x -> Gen y -> Gen (f x y)) -> Laws Source #

Tests the following Category laws:

Left Identity
id . ff
Right Identity
f . idf
Associativity
f . (g . h)(f . g) . h

commutativeCategoryLaws :: forall f. (Category f, forall x y. (Eq x, Eq y) => Eq (f x y), forall x y. (Show x, Show y) => Show (f x y)) => (forall x y. Gen x -> Gen y -> Gen (f x y)) -> Laws Source #

Tests the following Category laws:

Commutativity
f . gg . f

Defining your own Laws

data Laws Source #

A Laws is the name of the typeclass and the set of named properties associated with that typeclass.

Constructors

Laws 

data LawContext Source #

The context surrounding the property test of a law. Use contextualise to turn this into a Context.

Constructors

LawContext 

Fields

data Context Source #

You can provide a Context to heqCtx,heqCtx1,heqCtx2,hneqCtx,hneqCtx1,or hneqCtx2. The Context is used to provide useful error messages in the event of a failure.

Constructors

NoContext 
Context String 

Hedgehog equality tests sans source information

hLessThan :: (MonadTest m, Ord a, Show a, HasCallStack) => a -> a -> m () Source #

Fails the test if the right argument is less than or equal to the left. see https://github.com/hedgehogqa/haskell-hedgehog/pull/196

hGreaterThan :: (MonadTest m, Ord a, Show a, HasCallStack) => a -> a -> m () Source #

Fails the test if the right argument is greater than or equal to the left. see https://github.com/hedgehogqa/haskell-hedgehog/pull/196

heq :: (MonadTest m, HasCallStack, Eq a, Show a) => a -> a -> m () infix 4 Source #

Passes the test if the given arguments are equal. Otherwise fails with NoContext.

heq1 :: (MonadTest m, HasCallStack, Eq a, Show a, forall x. Eq x => Eq (f x), forall x. Show x => Show (f x)) => f a -> f a -> m () infix 4 Source #

Passes the test if the given arguments are equal. Otherwise fails with NoContext.

heq2 :: (MonadTest m, HasCallStack, Eq a, Eq b, Show a, Show b, forall x y. (Eq x, Eq y) => Eq (f x y), forall x y. (Show x, Show y) => Show (f x y)) => f a b -> f a b -> m () infix 4 Source #

Passes the test if the given arguments are equal. Otherwise fails with NoContext.

heqCtx :: (MonadTest m, HasCallStack, Eq a, Show a) => a -> a -> Context -> m () Source #

Passes the test if the given arguments are equal. Otherwise fails with the given Context.

heqCtx1 :: (MonadTest m, HasCallStack, Eq a, Show a, forall x. Eq x => Eq (f x), forall x. Show x => Show (f x)) => f a -> f a -> Context -> m () Source #

Passes the test if the given arguments are equal. Otherwise fails with the given Context.

heqCtx2 :: (MonadTest m, HasCallStack, Eq a, Eq b, Show a, Show b, forall x y. (Eq x, Eq y) => Eq (f x y), forall x y. (Show x, Show y) => Show (f x y)) => f a b -> f a b -> Context -> m () Source #

Passes the test if the given arguments are equal. Otherwise fails with the given Context.

hneq :: (MonadTest m, HasCallStack, Eq a, Show a) => a -> a -> m () infix 4 Source #

Passes the test if the given arguments are not equal. Otherwise fails with NoContext.

hneq1 :: (MonadTest m, HasCallStack, Eq a, Show a, forall x. Eq x => Eq (f x), forall x. Show x => Show (f x)) => f a -> f a -> m () Source #

Passes the test if the given arguments are not equal. Otherwise fails with NoContext.

hneq2 :: (MonadTest m, HasCallStack, Eq a, Eq b, Show a, Show b, forall x y. (Eq x, Eq y) => Eq (f x y), forall x y. (Show x, Show y) => Show (f x y)) => f a b -> f a b -> m () infix 4 Source #

Passes the test if the given arguments are not equal. Otherwise fails with NoContext.

hneqCtx :: (MonadTest m, HasCallStack, Eq a, Show a) => a -> a -> Context -> m () Source #

Passes the test if the given arguments are not equal. Otherwise fails with the given Context.

hneqCtx1 :: (MonadTest m, HasCallStack, Eq a, Show a, forall x. Eq x => Eq (f x), forall x. Show x => Show (f x)) => f a -> f a -> Context -> m () Source #

Passes the test if the given arguments are not equal. Otherwise fails with the given Context.

hneqCtx2 :: (MonadTest m, HasCallStack, Eq a, Eq b, Show a, Show b, forall x y. (Eq x, Eq y) => Eq (f x y), forall x y. (Show x, Show y) => Show (f x y)) => f a b -> f a b -> Context -> m () Source #

Passes the test if the given arguments are not equal. Otherwise fails with the given Context.