Skip to content

Commit 03da23a

Browse files
author
Arnaud Bailly
authored
Clarify Quantification usage and functions (#65)
* Clarify Quantification usage and functions * Update comments * Ensure withGenQ's predicate is consistent with generator Also adds some test to verify withGenQ produces a valid Property. This test should be generalised to various constructors and combinators producing Quantification * Output details of tests run by default * Updated CHANGELOG
1 parent 5ebbf12 commit 03da23a

File tree

6 files changed

+46
-9
lines changed

6 files changed

+46
-9
lines changed

cabal.project

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,7 @@ packages:
44

55
tests: true
66

7+
test-show-details: direct
8+
79
allow-newer:
8-
text
10+
text

quickcheck-dynamic/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ changes.
1313
* Added the option to return errors from actions by defining `type Error state`.
1414
When this is defined `perform` has return type `m (Either (Error state) (Realized m a))`,
1515
when it is left as the default the type remains `m (Realized m a)`.
16+
* Changed `withGenQ` to _require_ a predicate when defining a `Quantification`
1617

1718
## 3.3.1
1819

quickcheck-dynamic/quickcheck-dynamic.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ test-suite quickcheck-dynamic-test
9393
Spec.DynamicLogic.CounterModel
9494
Spec.DynamicLogic.Registry
9595
Spec.DynamicLogic.RegistryModel
96+
Test.QuickCheck.DynamicLogic.QuantifySpec
9697

9798
ghc-options: -rtsopts
9899
build-depends:

quickcheck-dynamic/src/Test/QuickCheck/DynamicLogic/Quantify.hs

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,22 @@ import Test.QuickCheck
3232
import Test.QuickCheck.DynamicLogic.CanGenerate
3333
import Test.QuickCheck.StateModel
3434

35-
-- | A `Quantification` over a type @a@ is a generator that can be used with
36-
-- `Plutus.Contract.Test.ContractModel.forAllQ` to generate random values in
37-
-- DL scenarios. In addition to a QuickCheck generator a `Quantification` contains a shrinking
38-
-- strategy that ensures that shrunk values stay in the range of the generator.
35+
-- | A `Quantification` over a type @a@ is a generator that can be used to generate random values in
36+
-- DL scenarios.
37+
--
38+
-- A `Quantification` is similar to a `Test.QuickCheck.Arbitrary`, it groups together:
39+
--
40+
-- * A standard QuickCheck _generator_ in the `Gen` monad, which can be "empty",
41+
-- * A _shrinking_ strategy for generated values in the case of a
42+
-- failures ensuring they stay within the domain,
43+
-- * A _predicate_ allowing finer grained control on generation
44+
-- and shrinking process, e.g in the case the range of the generator
45+
-- depends on trace context.
46+
--
47+
-- NOTE: Leaving the possibility of generating `Nothing` is useful to simplify the generation
48+
-- process for `elements` or `frequency` which may normally crash when the list to select
49+
-- elements from is empty. This makes writing `DL` formulas cleaner, removing the need to
50+
-- handle non-existence cases explicitly.
3951
data Quantification a = Quantification
4052
{ genQ :: Maybe (Gen a)
4153
, isaQ :: a -> Bool
@@ -51,10 +63,11 @@ generateQ q = fromJust (genQ q) `suchThat` isaQ q
5163
shrinkQ :: Quantification a -> a -> [a]
5264
shrinkQ q a = filter (isaQ q) (shrQ q a)
5365

54-
-- | Wrap a `Gen a` generator in a `Quantification a`.
55-
-- Uses given shrinker.
56-
withGenQ :: Gen a -> (a -> [a]) -> Quantification a
57-
withGenQ gen = Quantification (Just gen) (const True)
66+
-- | Construct a `Quantification a` from its constituents.
67+
-- Note the predicate provided is used to restrict both the range of values
68+
-- generated and the list of possible shrinked values.
69+
withGenQ :: Gen a -> (a -> Bool) -> (a -> [a]) -> Quantification a
70+
withGenQ gen isA = Quantification (Just $ gen `suchThat` isA) isA
5871

5972
-- | Pack up an `Arbitrary` instance as a `Quantification`. Treats all values as being in range.
6073
arbitraryQ :: Arbitrary a => Quantification a
@@ -228,6 +241,8 @@ instance Quantifiable a => Quantifiable [a] where
228241
from (x : xs) = (x, xs)
229242
from [] = error "quantify: impossible"
230243

244+
-- | Turns a `Quantification` into a `Property` to enable QuickChecking its
245+
-- validity.
231246
validQuantification :: Show a => Quantification a -> Property
232247
validQuantification q =
233248
forAllShrink (fromJust $ genQ q) (shrinkQ q) $ isaQ q

quickcheck-dynamic/test/Spec.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ module Main (main) where
44

55
import Spec.DynamicLogic.CounterModel qualified
66
import Spec.DynamicLogic.RegistryModel qualified
7+
import Test.QuickCheck.DynamicLogic.QuantifySpec qualified
78
import Test.Tasty
89

910
main :: IO ()
@@ -15,4 +16,5 @@ tests =
1516
"dynamic logic"
1617
[ Spec.DynamicLogic.RegistryModel.tests
1718
, Spec.DynamicLogic.CounterModel.tests
19+
, Test.QuickCheck.DynamicLogic.QuantifySpec.tests
1820
]
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
module Test.QuickCheck.DynamicLogic.QuantifySpec where
2+
3+
import Test.QuickCheck (Arbitrary (..), Gen, Property)
4+
import Test.QuickCheck.DynamicLogic.Quantify (validQuantification, withGenQ)
5+
import Test.Tasty (TestTree, testGroup)
6+
import Test.Tasty.QuickCheck (testProperty)
7+
8+
propWithGenQRestrictsValues :: Property
9+
propWithGenQRestrictsValues =
10+
validQuantification $ withGenQ (arbitrary :: Gen Int) ((< 10) . abs) (shrink @Int)
11+
12+
tests :: TestTree
13+
tests =
14+
testGroup
15+
"Quantification"
16+
[testProperty "withGenQ restricts possible generated values" propWithGenQRestrictsValues]

0 commit comments

Comments
 (0)