proarrow:testing
Safe HaskellNone
LanguageGHC2024

Proarrow.Testing

Description

Generic property-testing infrastructure for categories: Testable says how to generate and enumerate the objects of a kind, TestableProfunctor and TestableType how to generate values (using falsify generators), and TestingEqShow provides semantic equality and display for values without useful structural Eq/Show (functions, opaque morphisms). Instances for your own category plus the law checks in Proarrow.Testing.Laws give it a test suite.

Synopsis

Documentation

data GenTotal a where Source Github #

Constructors

GenEmpty :: forall a. ~(forall x. a -> x) -> GenTotal a 
GenNENonFun :: forall a. Gen a -> GenTotal a 
GenFun :: forall a1 b a. (TestableType a1, TestableType b) => ((a1 -> b) -> a) -> Gen (Fun (ShowP a1) (ShowP b)) -> GenTotal a 

Instances

Instances details
Alternative GenTotal Source Github # 
Instance details

Defined in Proarrow.Testing

Applicative GenTotal Source Github # 
Instance details

Defined in Proarrow.Testing

Methods

pure :: a -> GenTotal a Github #

(<*>) :: GenTotal (a -> b) -> GenTotal a -> GenTotal b Github #

liftA2 :: (a -> b -> c) -> GenTotal a -> GenTotal b -> GenTotal c Github #

(*>) :: GenTotal a -> GenTotal b -> GenTotal b Github #

(<*) :: GenTotal a -> GenTotal b -> GenTotal a Github #

Functor GenTotal Source Github # 
Instance details

Defined in Proarrow.Testing

Methods

fmap :: (a -> b) -> GenTotal a -> GenTotal b Github #

(<$) :: a -> GenTotal b -> GenTotal a Github #

Monad GenTotal Source Github # 
Instance details

Defined in Proarrow.Testing

Methods

(>>=) :: GenTotal a -> (a -> GenTotal b) -> GenTotal b Github #

(>>) :: GenTotal a -> GenTotal b -> GenTotal b Github #

return :: a -> GenTotal a Github #

invmap :: (a -> b) -> (b -> a) -> GenTotal a -> GenTotal b Source Github #

oneOfTotal :: [GenTotal a] -> GenTotal a Source Github #

Uniformly choose among any number of alternatives, dropping the empty ones. Plain <|> only combines two generators at 50/50, so chaining it over more than two alternatives associates pairwise and skews weight towards whichever branch ends up outermost in the resulting tree instead of splitting evenly — use this instead whenever there are more than two alternatives to pick fairly among.

class TestingEqShow a where Source Github #

Minimal complete definition

Nothing

Methods

eqP :: a -> a -> Property Bool Source Github #

default eqP :: Eq a => a -> a -> Property Bool Source Github #

showP :: a -> String Source Github #

default showP :: Show a => a -> String Source Github #

Instances

Instances details
TestingEqShow (Unit a b) Source Github # 
Instance details

Defined in Proarrow.Testing

Methods

eqP :: Unit a b -> Unit a b -> Property Bool Source Github #

showP :: Unit a b -> String Source Github #

(TestableType a, TestingEqShow b) => TestingEqShow (a -> b) Source Github # 
Instance details

Defined in Proarrow.Testing

Methods

eqP :: (a -> b) -> (a -> b) -> Property Bool Source Github #

showP :: (a -> b) -> String Source Github #

TestingEqShow (p b a) => TestingEqShow (Op p ('OP a) ('OP b)) Source Github # 
Instance details

Defined in Proarrow.Testing

Methods

eqP :: Op p ('OP a) ('OP b) -> Op p ('OP a) ('OP b) -> Property Bool Source Github #

showP :: Op p ('OP a) ('OP b) -> String Source Github #

(TestingEqShow (a ~> (f @ b)), Ob b) => TestingEqShow (Rep f a b) Source Github # 
Instance details

Defined in Proarrow.Testing

Methods

eqP :: Rep f a b -> Rep f a b -> Property Bool Source Github #

showP :: Rep f a b -> String Source Github #

(TestingEqShow (catk a1 b1), TestingEqShow (catj a2 b2)) => TestingEqShow ((catk :**: catj) '(a1, a2) '(b1, b2)) Source Github # 
Instance details

Defined in Proarrow.Testing

Methods

eqP :: (catk :**: catj) '(a1, a2) '(b1, b2) -> (catk :**: catj) '(a1, a2) '(b1, b2) -> Property Bool Source Github #

showP :: (catk :**: catj) '(a1, a2) '(b1, b2) -> String Source Github #

class TestingEqShow a => TestableType a where Source Github #

Methods

gen :: GenTotal a Source Github #

Instances

Instances details
(Ob a, Ob b) => TestableType (Unit a b) Source Github # 
Instance details

Defined in Proarrow.Testing

Methods

gen :: GenTotal (Unit a b) Source Github #

(Function a, TestableType a, TestableType b) => TestableType (a -> b) Source Github # 
Instance details

Defined in Proarrow.Testing

Methods

gen :: GenTotal (a -> b) Source Github #

TestableType (p b a) => TestableType (Op p ('OP a) ('OP b)) Source Github # 
Instance details

Defined in Proarrow.Testing

Methods

gen :: GenTotal (Op p ('OP a) ('OP b)) Source Github #

(TestableType (a ~> (f @ b)), Ob b) => TestableType (Rep f a b) Source Github # 
Instance details

Defined in Proarrow.Testing

Methods

gen :: GenTotal (Rep f a b) Source Github #

(TestableType (catk a1 b1), TestableType (catj a2 b2)) => TestableType ((catk :**: catj) '(a1, a2) '(b1, b2)) Source Github # 
Instance details

Defined in Proarrow.Testing

Methods

gen :: GenTotal ((catk :**: catj) '(a1, a2) '(b1, b2)) Source Github #

newtype ShowP a Source Github #

Supplies a Show instance derived from showP.

falsify's Show instance for Fun needs Show on both parameters, and we only ever have TestingEqShow. Rather than reinterpret a Fun a b at the wrapped type after the fact, GenFun generates at Fun (ShowP a) (ShowP b) from the start, so show applies directly and no coercion is involved. applyFunP unwraps on the way back out.

Constructors

ShowP 

Fields

Instances

Instances details
Function a => Function (ShowP a) Source Github # 
Instance details

Defined in Proarrow.Testing

Methods

function :: Gen b -> Gen (ShowP a :-> b) Github #

TestingEqShow a => Show (ShowP a) Source Github # 
Instance details

Defined in Proarrow.Testing

applyFunP :: Fun (ShowP a) (ShowP b) -> a -> b Source Github #

Apply a generated function, wrapping and unwrapping the ShowP it was generated at. Both directions are ordinary newtype constructor applications.

isGenNonEmpty :: TestableType a => Bool Source Github #

True if a type's generator is non-empty. A pure check on TestableTypes gen — it doesn't sample anything, so it's safe (and cheap) to call as many times as convenient, e.g. once in a genSuchThat predicate and again via the real gen/genNamed call that actually produces a value.

genSuchThat :: Gen key -> (key -> Bool) -> Gen key Source Github #

Resample genKey (cheaply, within Gen) up to maxTries times until isUsable accepts the draw, before ever asking Property to commit to a choice.

Property-level discard restarts the *whole* property from scratch (and can trip falsify's per-slot discard-ratio limit, aborting the entire test run early) — so when a later, dependent draw (e.g. "a morphism out of this object") is likely to be empty for a "bad" choice made here, it's far cheaper to reject that choice immediately, inside Gen, than to commit to it via Property and let the dependent draw discover the problem. A choice that's still unusable after maxTries attempts is returned anyway, so a genuinely unsatisfiable requirement still falls through to whatever ordinary discard the caller's own dependent generation triggers.

maxTries :: Int Source Github #

How many times genSuchThat resamples before giving up. There's no principled formula for this — it depends on how sparse the specific requirement being searched for is, which genSuchThat has no way to know in advance. 100 is a pragmatic default: comfortably more than the number of candidates a small test object palette usually offers (so a single unlucky pick is very unlikely to exhaust it), while still cheap, since each attempt is a plain Gen sample rather than a Property-level discard.

genObSuchThat :: Testable k => (Some k -> Bool) -> Property (Some k) Source Github #

genOb, but resampled (see genSuchThat) until isUsable accepts the object.

data SomeProfunctorElt (p :: j +-> k) where Source Github #

Constructors

SomeP :: forall {k} {j} (a :: k) (b :: j) (p :: j +-> k). (TestOb a, TestOb b) => p a b -> SomeProfunctorElt p 

Instances

Instances details
(forall (a :: k) (b :: j). (TestOb a, TestOb b) => TestingEqShow (p a b), Testable k, Testable j) => Show (SomeProfunctorElt p) Source Github # 
Instance details

Defined in Proarrow.Testing

someP :: forall {k} {j} p (a :: j) (b :: k). (Profunctor p, TestObIsOb j, TestObIsOb k) => p a b -> SomeProfunctorElt p Source Github #

class (forall (a :: k) (b :: j). (TestOb a, TestOb b) => TestableType (p a b)) => TestableTypeP (p :: j +-> k) Source Github #

Instances

Instances details
(forall (a :: k) (b :: j). (TestOb a, TestOb b) => TestableType (p a b)) => TestableTypeP (p :: j +-> k) Source Github # 
Instance details

Defined in Proarrow.Testing

class (Testable j, Testable k, Profunctor p, forall (a :: k) (b :: j). (TestOb a, TestOb b) => TestingEqShow (p a b)) => TestableProfunctor (p :: j +-> k) where Source Github #

Minimal complete definition

Nothing

Methods

genProfunctorElt :: String -> Property (SomeProfunctorElt p) Source Github #

The default implementation generates types a and b and then generates a value of type p a b. But that can cause too many discarded tests.

Instances

Instances details
TestableProfunctor Unit Source Github # 
Instance details

Defined in Proarrow.Testing

TestableProfunctor p => TestableProfunctor (Op p :: OPPOSITE j -> OPPOSITE k -> Type) Source Github # 
Instance details

Defined in Proarrow.Testing

(TestableProfunctor p, TestableProfunctor q) => TestableProfunctor (p :**: q :: (k1, k2) -> (j1, j2) -> Type) Source Github # 
Instance details

Defined in Proarrow.Testing

class (forall (a :: k). TestOb a => Ob' a, TestableProfunctor (Hom k), TestableTypeP (Hom k), CategoryOf k) => Testable k where Source Github #

Minimal complete definition

showOb, genSome

Associated Types

type TestOb (a :: k) Source Github #

type TestOb (a :: k) = Ob a

Methods

showOb :: forall (a :: k). TestOb a => String Source Github #

eqOb :: forall (a :: k) (b :: k). (TestOb a, TestOb b) => Maybe (a :~: b) Source Github #

default eqOb :: forall (a :: k) (b :: k). (TestOb a, TestOb b, Typeable a, Typeable b) => Maybe (a :~: b) Source Github #

genSome :: Gen (Some k) Source Github #

Instances

Instances details
Testable () Source Github # 
Instance details

Defined in Proarrow.Testing

Associated Types

type TestOb (a :: ()) 
Instance details

Defined in Proarrow.Testing

type TestOb (a :: ()) = Ob a

Methods

showOb :: forall (a :: ()). TestOb a => String Source Github #

eqOb :: forall (a :: ()) (b :: ()). (TestOb a, TestOb b) => Maybe (a :~: b) Source Github #

genSome :: Gen (Some ()) Source Github #

Testable k => Testable (OPPOSITE k) Source Github # 
Instance details

Defined in Proarrow.Testing

Methods

showOb :: forall (a :: OPPOSITE k). TestOb a => String Source Github #

eqOb :: forall (a :: OPPOSITE k) (b :: OPPOSITE k). (TestOb a, TestOb b) => Maybe (a :~: b) Source Github #

genSome :: Gen (Some (OPPOSITE k)) Source Github #

(Testable j, Testable k) => Testable (j, k) Source Github # 
Instance details

Defined in Proarrow.Testing

Methods

showOb :: forall (a :: (j, k)). TestOb a => String Source Github #

eqOb :: forall (a :: (j, k)) (b :: (j, k)). (TestOb a, TestOb b) => Maybe (a :~: b) Source Github #

genSome :: Gen (Some (j, k)) Source Github #

class TestOb a => TestOb' (a :: k) Source Github #

Instances

Instances details
TestOb a => TestOb' (a :: k) Source Github # 
Instance details

Defined in Proarrow.Testing

class (forall (a :: k). Ob a => TestOb' a) => TestObIsOb k Source Github #

Instances

Instances details
(forall (a :: k). Ob a => TestOb' a) => TestObIsOb k Source Github # 
Instance details

Defined in Proarrow.Testing

obFromTestOb :: forall {k} (a :: k) r. (Testable k, TestOb a) => (Ob a => r) -> r Source Github #

Recover Ob a from TestOb a (the Testable superclass entailment), packaged as a function so that call sites with other quantified givens in scope (e.g. the comonoid supply of a CopyDiscard category, whose head has Ob as a superclass) don't have to rely on GHC expanding superclasses of quantified-constraint heads. Observed on GHC 9.10.3: with such a given in scope, \r -> r at this type fails with "Could not deduce Ob a", while the same lambda compiles without it (cf. propSymMonoidal_ versus propCopyDiscard_). Likely a solver limitation; retry dropping this helper after a GHC upgrade.

data Some k where Source Github #

Constructors

Some :: forall {k} (a :: k). TestOb a => Some k 

Instances

Instances details
Testable k => Show (Some k) Source Github # 
Instance details

Defined in Proarrow.Testing

Methods

showsPrec :: Int -> Some k -> ShowS Github #

show :: Some k -> String Github #

showList :: [Some k] -> ShowS Github #

mapSome :: forall (f :: j -> k) -> (forall (a :: j). TestOb a => TestOb' (f a)) => Some j -> Some k Source Github #

class MkSomeList (as :: [k]) where Source Github #

Methods

mkSomeList :: [Some k] Source Github #

Instances

Instances details
MkSomeList ('[] :: [k]) Source Github # 
Instance details

Defined in Proarrow.Testing

Methods

mkSomeList :: [Some k] Source Github #

(TestOb a, MkSomeList as) => MkSomeList (a ': as :: [k]) Source Github # 
Instance details

Defined in Proarrow.Testing

Methods

mkSomeList :: [Some k] Source Github #

someElem :: Show a => [a] -> Property a Source Github #

someElemWith :: (a -> String) -> [a] -> Property a Source Github #

genSomeDef :: forall {k} (obs :: [k]). (Testable k, MkSomeList obs) => Gen (Some k) Source Github #

eqHask :: (TestableType a, TestingEqShow b) => (a -> b) -> (a -> b) -> Property Bool Source Github #

sampleP :: forall {j} {k} (p :: j +-> k). (Testable j, Testable k, TestableProfunctor p) => IO (Maybe String) Source Github #