Skip to content

Commit

Permalink
Get rid of magic behaviour where properties without quantification we…
Browse files Browse the repository at this point in the history
…re only tested once (e.g.: quickCheck True).

This behaviour has led to strange behaviour in the past and proved rather difficult to get right. Here is the latest case:

(1) quickCheck . checkCoverage $ coverTable ”A” [(”a”,1)] True
(2) quickCheck . checkCoverage $ coverTable ”A” [(”a”,1)] (\() -> True)

Property (1) succeeds, but property (2) continues testing forever.

It seems simpler to just remove this, and people can use `once` or `withMaxSuccess 1` to limit the number of tests explicitly. In practice, the only properties affected will be those of type IO Bool, which will now be tested 100 times rather than once.
  • Loading branch information
nick8325 committed Mar 7, 2024
1 parent 2d73ed7 commit c606b7b
Showing 1 changed file with 2 additions and 14 deletions.
16 changes: 2 additions & 14 deletions src/Test/QuickCheck/Property.hs
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,6 @@ newtype Property = MkProperty { unProperty :: Gen Prop }

-- | The class of properties, i.e., types which QuickCheck knows how to test.
-- Typically a property will be a function returning 'Bool' or 'Property'.
--
-- If a property does no quantification, i.e. has no
-- parameters and doesn't use 'forAll', it will only be tested once.
-- This may not be what you want if your property is an @IO Bool@.
-- You can change this behaviour using the 'again' combinator.
class Testable prop where
-- | Convert the thing to a property.
property :: prop -> Property
Expand Down Expand Up @@ -142,7 +137,7 @@ instance Testable Prop where
property p = MkProperty . return . protectProp $ p

instance Testable prop => Testable (Gen prop) where
property mp = MkProperty $ do p <- mp; unProperty (again p)
property mp = MkProperty $ do p <- mp; unProperty (property p)

instance Testable Property where
property (MkProperty mp) = MkProperty (fmap protectProp mp)
Expand All @@ -157,9 +152,6 @@ morallyDubiousIOProperty = ioProperty
-- Warning: any random values generated inside of the argument to @ioProperty@
-- will not currently be shrunk. For best results, generate all random values
-- before calling @ioProperty@, or use 'idempotentIOProperty' if that is safe.
--
-- Note: if your property does no quantification, it will only be tested once.
-- To test it repeatedly, use 'again'.
ioProperty :: Testable prop => IO prop -> Property
ioProperty prop = idempotentIOProperty (fmap noShrinking prop)

Expand Down Expand Up @@ -319,7 +311,7 @@ succeeded, failed, rejected :: Result
, expect = True
, reason = ""
, theException = Nothing
, abort = True
, abort = False
, maybeNumTests = Nothing
, maybeCheckCoverage = Nothing
, labels = []
Expand Down Expand Up @@ -839,7 +831,6 @@ forAllShrinkBlind
:: Testable prop
=> Gen a -> (a -> [a]) -> (a -> prop) -> Property
forAllShrinkBlind gen shrinker pf =
again $
MkProperty $
gen >>= \x ->
unProperty $
Expand All @@ -850,7 +841,6 @@ forAllShrinkBlind gen shrinker pf =
-- makes 100 random choices.
(.&.) :: (Testable prop1, Testable prop2) => prop1 -> prop2 -> Property
p1 .&. p2 =
again $
MkProperty $
arbitrary >>= \b ->
unProperty $
Expand All @@ -864,7 +854,6 @@ p1 .&&. p2 = conjoin [property p1, property p2]
-- | Take the conjunction of several properties.
conjoin :: Testable prop => [prop] -> Property
conjoin ps =
again $
MkProperty $
do roses <- mapM (fmap unProp . unProperty . property) ps
return (MkProp (conj id roses))
Expand Down Expand Up @@ -903,7 +892,6 @@ p1 .||. p2 = disjoin [property p1, property p2]
-- | Take the disjunction of several properties.
disjoin :: Testable prop => [prop] -> Property
disjoin ps =
again $
MkProperty $
do roses <- mapM (fmap unProp . unProperty . property) ps
return (MkProp (foldr disj (MkRose failed []) roses))
Expand Down

0 comments on commit c606b7b

Please sign in to comment.