quickcheck-property-comb (empty) → 0.1.0.0
raw patch · 4 files changed
+152/−0 lines, 4 filesdep +QuickCheckdep +basedep +mtl
Dependencies added: QuickCheck, base, mtl
Files
- LICENSE +1/−0
- README.md +82/−0
- Test/QuickCheck/Property/Comb.hs +41/−0
- quickcheck-property-comb.cabal +28/−0
+ LICENSE view
@@ -0,0 +1,1 @@+DWTFYWWI
+ README.md view
@@ -0,0 +1,82 @@+quickcheck-property-comb+--------+This is collection of combinators built on the Reader and Writer+Monads to allow for fast and painless Quickcheck property/invariant construction.++Why?+----+[Quickcheck](http://hackage.haskell.org/package/QuickCheck) is a tool used to+test cases based on constructed Properties,or essentially functions taking a+data structure and returning a boolean True or False. ++However when running tests, the only way to document their failing case+is through is through labeling them after binding, e.g.: ++```haskell+inv1, inv2, inv3 :: Foo -> Bool +..+fooInvariants :: Foo -> Property +fooInvariants f = + conjoin . map property $ + conjoin $ zipWith toLabeled+ ["foo should be even", "foo should contain 3 bar", "all bar should not equal foo"] + [inv1 f, inv2 f, inv3 f]+```++This gets unwieldy fast as the complexity of the data-structure increases, so+quickcheck-property-comb provides the following:+ - A clean separation of composition of invariants and the creation + and documenting of those invariants for determining cause of failure.+ - Effective diagnostics for invariants with changing post-conditions,+ leading to <b>faster cause-of-failure diagnosis</b>.++Example use+-----------+```haskell+data (Ord l) => QuantityConsumers l =+ QuantityConsumers {+ atQuantity :: S.Set l,+ qcMet :: M.Map (S.Set l) Bool,+ qcDisjoints :: Disjoints l+ }++disjoint_sizes :: Inv (Disjoints l)+disjoint_sizes = do+ doc . unlines $+ [+ "the intersection of all at quantity and disjoints are the only allowed",+ "singleton sets in disjoints"+ ]+ disjoints <- cause + -- Do some checking on disjoints + return False++disjoints_eq :: Inv (Disjoints l)+disjoints_eq = do+ doc "the solution state domain and sets formed by partition are equal"+ ..+ return False++disjoints :: Invariants (Disjoints l)+disjoints = do+ sat disjoints_eq+ sat disjoints_sizes++at_quantity_in_disjoint :: Inv (QuantityConsumers l)+at_quantity_in_disjoint = do+ doc "all at quantity are a singleton subset in disjoints"++ subsets <- (map S.singleton) . S.toList . atQuantity <$> cause+ disjoint_sets <- fromDisjoints <$> cause++ return . and . map ((flip S.member) disjoint_sets) $ subsets++inv_quantity_consumers :: Invariants (QuantityConsumers l)+inv_quantity_consumers = do+ satcomp qcDisjoints disjoints+ sat at_quantity_in_disjoint++-- Then to create the final property+prop_quantity_consumers :: QuantityConsumers l -> Property+prop_quantity_consumers q = runInvariants q inv_quantity_consumers+```
+ Test/QuickCheck/Property/Comb.hs view
@@ -0,0 +1,41 @@+-- TODO in some cases it may be useful to indent messages+-- based on an invariant category above them++{-# LANGUAGE TupleSections, ScopedTypeVariables, GeneralizedNewtypeDeriving #-}+module Test.QuickCheck.Property.Comb where+import Test.QuickCheck+import Control.Monad.Writer+import Control.Monad.Reader++--A Monad which collects related invariants on the input+type InvM i r = ReaderT i (Writer String) r+type Inv i = InvM i Bool++cause :: (Monad m) => ReaderT i m i+cause = ask++--A Monad which collects distinct invariants on the input+type Invariants i = ReaderT i (Writer [Inv i]) ()++doc :: String -> InvM r ()+doc = lift . tell++sat :: Inv i -> Invariants i+sat p = lift . tell $ (p:[])++satcomp :: forall i i'. (i' -> i) -> Invariants i -> Invariants i'+satcomp f = mapReaderT toWriter . withReaderT f where+ toWriter :: Writer [Inv i] () -> Writer [Inv i'] ()+ toWriter orig = mapWriter (((),) . map toPredicate . snd) orig+ toPredicate :: Inv i -> Inv i'+ toPredicate orig = withReaderT f orig++runInv :: (Show i) => i -> Inv i -> Property+runInv cause' rdr =+ let (effect, msg) = runWriter . (runReaderT rdr) $ cause' in+ printTestCase ("inv: " ++ if null msg then "unknown" else msg)+ . property $ effect++runInvariants :: forall i. (Show i) => i -> Invariants i -> Property+runInvariants cause' preds =+ conjoin . map (runInv cause') . execWriter . runReaderT preds $ cause'
+ quickcheck-property-comb.cabal view
@@ -0,0 +1,28 @@+-- Initial quickcheck-property-comb.cabal generated by cabal init. For +-- further documentation, see http://haskell.org/cabal/users-guide/++name: quickcheck-property-comb+version: 0.1.0.0+synopsis: Combinators for Quickcheck Property construction and Property diagnostics+description: This project aims to reduce the pain of composing+ invariants/properties, and the documenting of those invariants for determining the cause+ of failure. Additionally, it intends to provide effective diagnostics for+ invariants with changing post-conditions, leading to faster cause-of-failure+ diagnosis.++homepage: http://www.github.com/jfeltz/quickcheck-property-comb+license: PublicDomain+license-file: LICENSE+author: John Feltz+maintainer: jfeltz@gmail.com+category: Testing+build-type: Simple+cabal-version: >=1.10++library+ exposed-modules: Test.QuickCheck.Property.Comb+ -- other-modules: + other-extensions: TupleSections, ScopedTypeVariables, GeneralizedNewtypeDeriving+ build-depends: base >=4.5 && <4.6, QuickCheck >=2.5 && <2.6, mtl >=2.1 && <2.2+ hs-source-dirs: ./ + default-language: Haskell2010