checkers (empty) → 0.1
raw patch · 23 files changed
+1443/−0 lines, 23 filesdep +QuickCheckdep +arraydep +basesetup-changed
Dependencies added: QuickCheck, array, base, random
Files
- Makefile +2/−0
- README +21/−0
- Setup.lhs +3/−0
- changes.tw +5/−0
- checkers.cabal +54/−0
- src/Control/Monad/Extensions.hs +10/−0
- src/Test/QuickCheck/Applicative.hs +8/−0
- src/Test/QuickCheck/Bottoms.hs +34/−0
- src/Test/QuickCheck/Checkers.hs +448/−0
- src/Test/QuickCheck/Classes.hs +460/−0
- src/Test/QuickCheck/Instances.hs +20/−0
- src/Test/QuickCheck/Instances/Array.hs +10/−0
- src/Test/QuickCheck/Instances/Char.hs +75/−0
- src/Test/QuickCheck/Instances/Eq.hs +12/−0
- src/Test/QuickCheck/Instances/Int.hs +21/−0
- src/Test/QuickCheck/Instances/List.hs +72/−0
- src/Test/QuickCheck/Instances/Maybe.hs +9/−0
- src/Test/QuickCheck/Instances/Num.hs +28/−0
- src/Test/QuickCheck/Instances/Ord.hs +10/−0
- src/Test/QuickCheck/Instances/Tuple.hs +25/−0
- src/Test/QuickCheck/Instances/Word.hs +21/−0
- src/Test/QuickCheck/Later.hs +82/−0
- wikipage.tw +13/−0
+ Makefile view
@@ -0,0 +1,2 @@+# On code.haskell.org+include ../cho-cabal-make.inc
+ README view
@@ -0,0 +1,21 @@+Checkers [1] is a library for reusable QuickCheck properties, particularly+for standard type classes (class laws and class morphisms [2]). Most of+Reactive [3] can be specified and tested using just these properties.+Also lots of support for randomly generating data values (thanks to Thomas+Davie).++Please share any comments & suggestions on the discussion (talk) page at+[1].++You can configure, build, and install all in the usual way with Cabal+commands.++ runhaskell Setup.lhs configure+ runhaskell Setup.lhs build+ runhaskell Setup.lhs install++References:++[1] http://haskell.org/haskellwiki/checkers+[2] http://conal.net/papers/simply-reactive+[3] http://haskell.org/haskellwiki/reactive
+ Setup.lhs view
@@ -0,0 +1,3 @@+#!/usr/bin/env runhaskell+> import Distribution.Simple+> main = defaultMain
+ changes.tw view
@@ -0,0 +1,5 @@+== Version 0 ==++=== Version 0.0 ===++*
+ checkers.cabal view
@@ -0,0 +1,54 @@+Name: checkers+Version: 0.1+Cabal-Version: >= 1.2+Synopsis: Check properties on standard classes and data structures.+Category: Testing+Description:+ ''Checkers'' wraps up the expected properties associated with various+ standard type classes as QuickCheck properties. Also some morphism+ properties. It also provides arbitrary instances and generator combinators+ for common data types.+ .+ Project wiki page: <http://haskell.org/haskellwiki/checkers>+ .+ The module documentation pages have links to colorized source code and+ to wiki pages where you can read and contribute user comments. Enjoy!+ .+ © 2008 by Conal Elliott; BSD3 license.+ .+ Contributions from: Thomas Davie.+Author: Conal Elliott +Maintainer: conal@conal.net+Homepage: http://haskell.org/haskellwiki/checkers+Package-Url: http://code.haskell.org/checkers+Copyright: (c) 2008 by Conal Elliott+License: BSD3+Stability: experimental+build-type: Simple++Library+ hs-Source-Dirs: src+ Extensions:+ Build-Depends: base, random, QuickCheck < 2.0, array >= 0.1+ Exposed-Modules: + Test.QuickCheck.Checkers+ Test.QuickCheck.Applicative+ Test.QuickCheck.Classes+ Test.QuickCheck.Bottoms+ Test.QuickCheck.Instances+ Test.QuickCheck.Instances.Array+ Test.QuickCheck.Instances.Char+ Test.QuickCheck.Instances.Eq+ Test.QuickCheck.Instances.Int+ Test.QuickCheck.Instances.List+ Test.QuickCheck.Instances.Maybe+ Test.QuickCheck.Instances.Num+ Test.QuickCheck.Instances.Ord+ Test.QuickCheck.Instances.Tuple+ Test.QuickCheck.Instances.Word+ Test.QuickCheck.Later+ Other-modules:+ Control.Monad.Extensions+ ghc-options: -Wall -fno-warn-orphans++-- ghc-prof-options: -prof -auto-all
+ src/Control/Monad/Extensions.hs view
@@ -0,0 +1,10 @@+module Control.Monad.Extensions (satisfiesM,if') where++import Control.Applicative+import Control.Monad++satisfiesM :: Monad m => (a -> Bool) -> m a -> m a+satisfiesM p x = x >>= if' p return (const (satisfiesM p x))++if' :: Applicative f => f Bool -> f a -> f a -> f a+if' = liftA3 (\ c t e -> if c then t else e)
+ src/Test/QuickCheck/Applicative.hs view
@@ -0,0 +1,8 @@+{-# OPTIONS_GHC -Wall -fno-warn-orphans #-}+module Test.QuickCheck.Applicative where++import Test.QuickCheck+import Control.Monad+import Control.Applicative++instance Applicative Gen where { pure = return ; (<*>) = ap }
+ src/Test/QuickCheck/Bottoms.hs view
@@ -0,0 +1,34 @@+module Test.QuickCheck.Bottoms (bottom,infiniteComp) where++import Test.QuickCheck++import Control.Monad (forever)+import System.IO.Unsafe+import Control.Concurrent++bottom :: Gen a+bottom = return undefined++infiniteComp :: Gen a+infiniteComp = return hang++-- Without using unsafePerformIO, is there a way to define a+-- non-terminating but non-erroring pure value that consume very little+-- resources while not terminating?++-- | Never yield an answer. Like 'undefined' or 'error "whatever"', but+-- don't raise an error, and don't consume computational resources.+hang :: a+hang = unsafePerformIO hangIO++-- | Block forever+hangIO :: IO a+hangIO = do -- putStrLn "warning: blocking forever."+ -- Any never-terminating computation goes here+ -- This one can yield an exception "thread blocked indefinitely"+ -- newEmptyMVar >>= takeMVar+ -- sjanssen suggests this alternative:+ forever $ threadDelay maxBound+ -- forever's return type is (), though it could be fully+ -- polymorphic. Until it's fixed, I need the following line.+ return undefined
+ src/Test/QuickCheck/Checkers.hs view
@@ -0,0 +1,448 @@+{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances+ , FlexibleContexts, TypeSynonymInstances, GeneralizedNewtypeDeriving+ , UndecidableInstances, ScopedTypeVariables+ #-}+{-# OPTIONS_GHC -Wall -fno-warn-orphans #-}++----------------------------------------------------------------------+-- |+-- Module : Test.QuickCheck.Checkers+-- Copyright : (c) Conal Elliott 2007,2008+-- License : BSD3+-- +-- Maintainer : conal@conal.net+-- Stability : experimental+-- +-- Some QuickCheck helpers+----------------------------------------------------------------------++module Test.QuickCheck.Checkers+ (+ -- * Misc+ Test, TestBatch, unbatch, checkBatch, quickBatch, verboseBatch+ , probablisticPureCheck+ , Unop, Binop, genR, inverseL, inverse+ , FracT, NumT, OrdT, T+ -- * Generalized equality+ , EqProp(..), eq+ , leftId, rightId, bothId, isAssoc, isCommut, commutes+ , MonoidD, monoidD, endoMonoidD, homomorphism+ , idempotent, idempotent2, idemElem+ -- , funEq, AsFun(..)+ -- * Model-based (semantics-based) testing+ , Model(..)+ , meq, meq1, meq2, meq3, meq4, meq5+ , eqModels+ , Model1(..)+ -- * Some handy testing types+ , Positive, NonZero(..), NonNegative(..)+ , suchThat, suchThatMaybe+ , arbs, gens+ , (.&.)+ , arbitrarySatisfying+ ) where++-- import Data.Function (on)+import Data.Monoid+import Data.Function (on)+import Control.Applicative+import Control.Arrow ((***),first)+import Data.List (foldl')+import System.Random+import Test.QuickCheck+import System.IO.Unsafe++import Test.QuickCheck.Utils+import Test.QuickCheck.Applicative ()+import Test.QuickCheck.Instances.Num+import Control.Monad.Extensions+++-- import qualified Data.Stream as S+++{----------------------------------------------------------+ Misc+----------------------------------------------------------}++-- | Named test+type Test = (String,Property)++-- | Named batch of tests+type TestBatch = (String,[Test])++-- | Flatten a test batch for inclusion in another+unbatch :: TestBatch -> [Test]+unbatch (batchName,props) = map (first ((batchName ++ ": ")++)) props++-- TODO: consider a tree structure so that flattening is unnecessary.++-- | Run a batch of tests. See 'quickBatch' and 'verboseBatch'.+checkBatch :: Config -> TestBatch -> IO ()+checkBatch config (name,tests) =+ do putStrLn $ "\n" ++ name ++ ":"+ mapM_ pr tests+ where+ pr (s,p) = do putStr (padTo (width + 4) (" "++s ++ ":"))+ catch (check config p) print+ width = foldl' max 0 (map (length.fst) tests)++padTo :: Int -> String -> String+padTo n = take n . (++ repeat ' ')++-- | Check a batch tersely.+quickBatch :: TestBatch -> IO ()+quickBatch = checkBatch quick'+ +-- | Check a batch verbosely.+verboseBatch :: TestBatch -> IO ()+verboseBatch = checkBatch verbose'++quick', verbose' :: Config+quick' = defaultConfig { configMaxTest = 500 }+verbose' = quick' { configEvery = \ n args -> show n ++ ":\n" ++ unlines args }++{-++-- TODO: change TestBatch to be hierarchical/recursive, rather than+-- two-level.++data Batch n t = Test t | Batch [LBatch n t]+type LBatch n t = (n, Batch n t)++-- | Run a batch of tests. See 'quickBatch' and 'verboseBatch'.+checkL :: Config -> LBatch -> IO ()+checkL config = checkL' 0+ where+ checkL' :: Int -> LBatch -> IO ()+ ...+-}++-- | Unary function, handy for type annotations+type Unop a = a -> a++-- | Binary function, handy for type annotations+type Binop a = a -> a -> a++-- Testing types++-- | Token 'Fractional' type for tests+type FracT = Float+-- | Token 'Num' type for tests+type NumT = Int+-- | Token 'Ord' type for tests+type OrdT = Char+-- | Token uninteresting type for tests+type T = Char++genR :: Random a => (a, a) -> Gen a+genR (lo,hi) = fmap (fst . randomR (lo,hi)) rand+++-- | @f@ is a left inverse of @g@. See also 'inverse'.+inverseL :: (EqProp b, Arbitrary b, Show b) =>+ (a -> b) -> (b -> a) -> Property+f `inverseL` g = f . g =-= id ++-- | @f@ is a left and right inverse of @g@. See also 'inverseL'.+inverse :: ( EqProp a, Arbitrary a, Show a+ , EqProp b, Arbitrary b, Show b ) =>+ (a -> b) -> (b -> a) -> Property+f `inverse` g = f `inverseL` g .&. g `inverseL` f+++{----------------------------------------------------------+ Generalized equality+----------------------------------------------------------}++infix 4 =-=++-- | Types of values that can be tested for equality, perhaps through+-- random sampling.+class EqProp a where (=-=) :: a -> a -> Property++-- | For 'Eq' types as 'EqProp' types+eq :: Eq a => a -> a -> Property+a `eq` a' = property (a == a')++-- Template: fill in with Eq types for a+-- instance EqProp a where (=-=) = eq+-- E.g.,++instance EqProp Bool where (=-=) = eq+instance EqProp Char where (=-=) = eq+instance EqProp Int where (=-=) = eq+instance EqProp Double where (=-=) = eq++-- Lists+instance EqProp a => EqProp [a] where+ [] =-= [] = property True+ x:xs =-= y:ys = x =-= y .&. xs =-= ys+ _ =-= _ = property False++-- Maybe+instance EqProp a => EqProp (Maybe a) where+ Nothing =-= Nothing = property True+ Just x =-= Just y = x =-= y+ _ =-= _ = property False++-- Pairing+instance (EqProp a, EqProp b) => EqProp (a,b) where+ (a,b) =-= (a',b') = a =-= a' .&. b =-= b'++-- Either+instance (EqProp a, EqProp b) => EqProp (Either a b) where+ (Left x) =-= (Left x') = x =-= x'+ (Right x) =-= (Right x') = x =-= x'+ _ =-= _ = property False++-- Function equality+instance (Show a, Arbitrary a, EqProp b) => EqProp (a -> b) where+ f =-= f' = property (liftA2 (=-=) f f')+-- Alternative definition:+-- instance (Show a, Arbitrary a, EqProp b) => EqProp (a -> b) where+-- f =-= f' = property (probablisticPureCheck defaultConfig+-- (\x -> f x =-= g x))++eqModels :: (Model a b, EqProp b) => a -> a -> Property+eqModels = (=-=) `on` model++-- Other types+-- instance EqProp a => EqProp (S.Stream a) where (=-=) = eqModels+++-- | Has a given left identity, according to '(=-=)'+leftId :: (Show a, Arbitrary a, EqProp a) => (i -> a -> a) -> i -> Property+leftId op i = (i `op`) =-= id++-- | Has a given right identity, according to '(=-=)'+rightId :: (Show a, Arbitrary a, EqProp a) => (a -> i -> a) -> i -> Property+rightId op i = (`op` i) =-= id++-- | Has a given left and right identity, according to '(=-=)'+bothId :: (Show a, Arbitrary a, EqProp a) => (a -> a -> a) -> a -> Property+bothId = (liftA2.liftA2) (.&.) leftId rightId++-- bothId op i = leftId op i .&. rightId op i++-- | Associative, according to '(=-=)'+isAssoc :: (EqProp a, Show a, Arbitrary a) => (a -> a -> a) -> Property+isAssoc = isAssociativeBy (=-=) arbitrary++-- | Commutative, according to '(=-=)'+commutes :: EqProp z => (a -> a -> z) -> a -> a -> Property+commutes (#) a b = a # b =-= b # a++-- | Commutative, according to '(=-=)'+isCommut :: (EqProp a, Show a, Arbitrary a) => (a -> a -> a) -> Property+isCommut = isCommutableBy (=-=) arbitrary++-- | Explicit 'Monoid' dictionary. Doesn't have to correspond to an+-- actual 'Monoid' instance, though see 'monoidD'.+data MonoidD a = MonoidD a (a -> a -> a)++-- | 'Monoid' dictionary built from the 'Monoid' methods.+monoidD :: Monoid a => MonoidD a+monoidD = MonoidD mempty mappend++-- | Monoid dictionary for an unwrapped endomorphism. See also 'monoidD'+-- and 'Endo'.+endoMonoidD :: MonoidD (a -> a)+endoMonoidD = MonoidD id (.)++-- | Homomorphism properties with respect to given monoid dictionaries.+-- See also 'monoidMorphism'.+homomorphism :: (EqProp b, Show a, Arbitrary a) =>+ MonoidD a -> MonoidD b -> (a -> b) -> [(String,Property)]+homomorphism (MonoidD ida opa) (MonoidD idb opb) q =+ [ ("identity" , q ida =-= idb)+ , ("binop", property $ \ u v -> q (u `opa` v) =-= q u `opb` q v)+ ]++-- | The unary function @f@ is idempotent, i.e., @f . f == f@+idempotent :: (Show a, Arbitrary a, EqProp a) =>+ (a -> a) -> Property+idempotent f = idemElem (.) f++-- | A binary function @op@ is idempotent, i.e., @x `op` x == x@, for all @x@+idempotent2 :: (Show a, Arbitrary a, EqProp a) =>+ (a -> a -> a) -> Property+idempotent2 = property . idemElem++-- | A binary function @op@ is has an idempotent element @x@, i.e.,+-- @x `op` x == x@+idemElem :: EqProp a => (a -> a -> a) -> a -> Property+idemElem op x = x `op` x =-= x++{-+-- TODO: phase out AsFun, in favor of Model. withArray++-- | Types that can be modeled as functions.+class AsFun h a b | h -> a b where+ asFun :: h -> (a -> b)++instance AsFun (a->b) a b where asFun = id++-- | Equality of function-like types+funEq :: (AsFun h a b, EqProp (a -> b)) => h -> h -> Property+h `funEq` h' = asFun h =-= asFun h'+-}+++{----------------------------------------------------------+ Model-based (semantics-based) testing+----------------------------------------------------------}++---- From bytestring++class Model a b | a -> b where+ model :: a -> b -- get the model from a concrete value++-- note: bytestring doesn't make the fundep++---- Compare representation-level and model-level operations (commuting diagrams)++meq :: (Model a b, EqProp b) => a -> b -> Property+meq1 :: (Model a b, Model a1 b1, EqProp b) =>+ (a1 -> a) -> (b1 -> b) -> a1 -> Property+meq2 :: (Model a b, Model a1 b1, Model a2 b2, EqProp b) =>+ (a1 -> a2 -> a) -> (b1 -> b2 -> b) -> a1 -> a2 -> Property+meq3 :: (Model a b, Model a1 b1, Model a2 b2, Model a3 b3, EqProp b) =>+ (a1 -> a2 -> a3 -> a)+ -> (b1 -> b2 -> b3 -> b)+ -> a1 -> a2 -> a3 -> Property+meq4 :: ( Model a b, Model a1 b1, Model a2 b2+ , Model a3 b3, Model a4 b4, EqProp b) =>+ (a1 -> a2 -> a3 -> a4 -> a)+ -> (b1 -> b2 -> b3 -> b4 -> b)+ -> a1 -> a2 -> a3 -> a4 -> Property+meq5 :: ( Model a b, Model a1 b1, Model a2 b2, Model a3 b3+ , Model a4 b4, Model a5 b5, EqProp b) =>+ (a1 -> a2 -> a3 -> a4 -> a5 -> a)+ -> (b1 -> b2 -> b3 -> b4 -> b5 -> b)+ -> a1 -> a2 -> a3 -> a4 -> a5 -> Property++meq a b =+ model a =-= b+meq1 f g = \a ->+ model (f a) =-= g (model a)+meq2 f g = \a b ->+ model (f a b) =-= g (model a) (model b)+meq3 f g = \a b c ->+ model (f a b c) =-= g (model a) (model b) (model c)+meq4 f g = \a b c d ->+ model (f a b c d) =-= g (model a) (model b) (model c) (model d)+meq5 f g = \a b c d e ->+ model (f a b c d e) =-= g (model a) (model b) (model c) (model d) (model e)+++---- Some model instances++instance Model Bool Bool where model = id+instance Model Char Char where model = id+instance Model Int Int where model = id+instance Model Float Float where model = id+instance Model Double Double where model = id+instance Model String String where model = id++-- This next one requires UndecidableInstances+instance (Model a b, Model a' b') => Model (a,a') (b,b') where+ model = model *** model++-- instance Model (S.Stream a) (NonNegative Int -> a) where+-- model s (NonNegative i) = s S.!! i+++-- | Like 'Model' but for unary type constructors.+class Model1 f g | f -> g where+ model1 :: forall a. f a -> g a+++{----------------------------------------------------------+ Some handy testing types+----------------------------------------------------------}++-- from QC2, plus tweaks++type Positive a = NonZero (NonNegative a)++newtype NonZero a = NonZero { unNonZero :: a }+ deriving ( Eq, Ord, Num, Integral, Real, Enum, Show, Read )++instance (Num a, Arbitrary a) => Arbitrary (NonZero a) where+ arbitrary = fmap NonZero $ arbitrary `suchThat` (/= 0)+ coarbitrary = coarbitrary . unNonZero++newtype NonNegative a = NonNegative { unNonNegative :: a }+ deriving ( Eq, Ord, Num, Integral, Real, Enum, Show, Read )++instance (Num a, Arbitrary a) => Arbitrary (NonNegative a) where+ arbitrary = nonNegative+ coarbitrary = coarbitrary . unNonNegative++arbitrarySatisfying :: Arbitrary a => (a -> Bool) -> Gen a+arbitrarySatisfying = (arbitrary `suchThat`)++-- | Generates a value that satisfies a predicate.+suchThat :: Gen a -> (a -> Bool) -> Gen a+gen `suchThat` p = satisfiesM p gen++-- | Tries to generate a value that satisfies a predicate.+suchThatMaybe :: Gen a -> (a -> Bool) -> Gen (Maybe a)+gen `suchThatMaybe` p = sized (try 0 . max 1)+ where+ try _ 0 = return Nothing+ try k n = do x <- resize (2*k+n) gen+ if p x then return (Just x) else try (k+1) (n-1)++-- | Generate n arbitrary values+arbs :: Arbitrary a => Int -> IO [a]+arbs n = fmap (\ rnd -> generate n rnd (vector n)) newStdGen++-- | Produce n values from a generator+gens :: Int -> Gen a -> IO [a]+gens n gen =+ fmap (\ rnd -> generate 1000 rnd (sequence (replicate n gen))) newStdGen++-- The next two are from twanvl:++infixr 3 .&.+-- | Property conjunction+(.&.) :: (Testable prop1, Testable prop2) => prop1 -> prop2 -> Property+p1 .&. p2 = property $ \b -> if b then property p1 else property p2++instance Testable a => Testable [a] where+ property [] = property True+ property props = property $ \n -> props !! (n `mod` len)+ where len = length props++instance (Testable a, Testable b) => Testable (a,b) where+ property = uncurry (.&.)++probablisticPureCheck :: Testable a => Config -> a -> Bool+probablisticPureCheck config a = unsafePerformIO $+ do rnd <- newStdGen+ probablisticPureTests config (evaluate a) rnd 0 0 []++probablisticPureTests :: Config+ -> Gen Result+ -> StdGen+ -> Int+ -> Int+ -> [[String]]+ -> IO Bool+probablisticPureTests config gen rnd0 ntest nfail stamps+ | ntest == configMaxTest config = return True+ | nfail == configMaxFail config = return True+ | otherwise =+ case ok result of+ Nothing ->+ probablisticPureTests config gen rnd1 ntest (nfail+1) stamps+ Just True ->+ probablisticPureTests config gen rnd1 (ntest+1) nfail+ (stamp result:stamps)+ Just False ->+ return False+ where+ result = generate (configSize config ntest) rnd2 gen+ (rnd1,rnd2) = split rnd0
+ src/Test/QuickCheck/Classes.hs view
@@ -0,0 +1,460 @@+{-# LANGUAGE ScopedTypeVariables, FlexibleContexts, KindSignatures+ , Rank2Types, TypeOperators+ #-}++{-# OPTIONS_GHC -Wall #-}+----------------------------------------------------------------------+-- |+-- Module : Test.QuickCheck.Classes+-- Copyright : (c) Conal Elliott 2008+-- License : BSD3+-- +-- Maintainer : conal@conal.net+-- Stability : experimental+-- +-- Some QuickCheck properties for standard type classes+----------------------------------------------------------------------++module Test.QuickCheck.Classes+ (+ monoid, monoidMorphism, semanticMonoid+ , functor, functorMorphism, semanticFunctor, functorMonoid+ , applicative, applicativeMorphism, semanticApplicative+ , monad, monadMorphism, semanticMonad, monadFunctor+ , monadApplicative, arrow, arrowChoice, traversable+ , monadPlus, monadOr+ )+ where++import Data.Monoid+import Data.Foldable (foldMap)+import Data.Traversable (Traversable (..), fmapDefault, foldMapDefault)+import Control.Applicative+import Control.Monad (MonadPlus (..), ap, join)+import Control.Arrow (Arrow,ArrowChoice,first,second,left,right,(>>>),arr)+import Test.QuickCheck+import Text.Show.Functions ()++import Test.QuickCheck.Checkers+import Test.QuickCheck.Instances.Char ()+++-- | Properties to check that the 'Monoid' 'a' satisfies the monoid+-- properties. The argument value is ignored and is present only for its+-- type.+monoid :: forall a. (Monoid a, Show a, Arbitrary a, EqProp a) =>+ a -> TestBatch+monoid = const ( "monoid"+ , [ ("left identity", leftId mappend (mempty :: a))+ , ("right identity", rightId mappend (mempty :: a))+ , ("associativity" , isAssoc (mappend :: Binop a))+ ]+ )++-- | Monoid homomorphism properties. See also 'homomorphism'.+monoidMorphism :: (Monoid a, Monoid b, EqProp b, Show a, Arbitrary a) =>+ (a -> b) -> TestBatch+monoidMorphism q = ("monoid morphism", homomorphism monoidD monoidD q)++semanticMonoid :: forall a b.+ ( Model a b+ , Monoid a+ , Monoid b+ , Show a+ , Arbitrary a+ , EqProp b+ ) =>+ a -> TestBatch+semanticMonoid = const (first ("semantic " ++)+ (monoidMorphism (model:: a -> b)))++functorMonoid :: forall m a b.+ ( Functor m+ , Monoid (m a)+ , Monoid (m b)+ , Arbitrary (a->b)+ , Arbitrary (m a)+ , Show (m a)+ , EqProp (m b)) =>+ m (a,b) -> TestBatch+functorMonoid = const ("functor-monoid"+ , [ ( "identity",property identityP )+ , ( "binop", property binopP )+ ]+ )+ where+ identityP :: (a->b) -> Property+ identityP f = (fmap f) (mempty :: m a) =-= (mempty :: m b)+ binopP :: (a->b) -> (m a) -> (m a) -> Property+ binopP f u v = (fmap f) (u `mappend` v) =-= (fmap f u) `mappend` (fmap f v)++-- <camio> There I have an attempt at doing this. I eventually implemented +-- those semanticMorphisms as their own functions. I'm not too thrilled with+-- that implementation, but it works.++-- TODO: figure out out to eliminate the redundancy.++-- | Properties to check that the 'Functor' @m@ satisfies the functor+-- properties.+functor :: forall m a b c.+ ( Functor m+ , Arbitrary a, Arbitrary b, Arbitrary c+ , Show (m a), Arbitrary (m a), EqProp (m a), EqProp (m c)) =>+ m (a,b,c) -> TestBatch+functor = const ( "functor"+ , [ ("identity", property identityP)+ , ("compose" , property composeP) ]+ )+ where+ identityP :: Property+ composeP :: (b -> c) -> (a -> b) -> Property+ + identityP = fmap id =-= (id :: m a -> m a)+ composeP g f = fmap g . fmap f =-= (fmap (g.f) :: m a -> m c)++-- Note the similarity between 'functor' and 'monoidMorphism'. The+-- functor laws say that 'fmap' is a homomorphism w.r.t '(.)':+-- +-- functor = const ("functor", homomorphism endoMonoidD endoMonoidD fmap)+-- +-- However, I don't think the types can work out, since 'fmap' is used at+-- three different types.+++-- | 'Functor' morphism (natural transformation) properties+functorMorphism :: forall f g.+ ( Functor f, Functor g+ , Arbitrary (f NumT), Show (f NumT)+ , EqProp (g T)+ ) =>+ (forall a. f a -> g a) -> TestBatch+functorMorphism q = ("functor morphism", [("fmap", property fmapP)])+ where+ -- fmapP :: (NumT -> T) -> f NumT -> Property+ -- fmapP h l = q (fmap h l) =-= fmap h (q l)+ fmapP :: (NumT -> T) -> Property+ fmapP h = q . fmap h =-= fmap h . q++-- Note: monomorphism prevent us from saying @commutes (.) q (fmap h)@,+-- since @fmap h@ is used at two different types.++semanticFunctor :: forall f g.+ ( Model1 f g+ , Functor f+ , Functor g+ , Arbitrary (f NumT)+ , Show (f NumT)+ , EqProp (g T)+ ) =>+ f () -> TestBatch+semanticFunctor = const (functorMorphism (model1 :: forall b. f b -> g b))+++-- | Properties to check that the 'Applicative' @m@ satisfies the monad+-- properties+applicative :: forall m a b c.+ ( Applicative m+ , Arbitrary a, Arbitrary b, Arbitrary (m a)+ , Arbitrary (m (b -> c)), Show (m (b -> c))+ , Arbitrary (m (a -> b)), Show (m (a -> b))+ , Show a, Show (m a)+ , EqProp (m a), EqProp (m b), EqProp (m c)+ ) =>+ m (a,b,c) -> TestBatch+applicative = const ( "applicative"+ , [ ("identity" , property identityP)+ , ("composition" , property compositionP)+ , ("homomorphism", property homomorphismP)+ , ("interchange" , property interchangeP)+ , ("functor" , property functorP)+ ]+ )+ where+ identityP :: m a -> Property+ compositionP :: m (b -> c) -> m (a -> b) -> m a -> Property+ homomorphismP :: (a -> b) -> a -> Property+ interchangeP :: m (a -> b) -> a -> Property+ functorP :: (a -> b) -> m a -> Property+ + identityP v = (pure id <*> v) =-= v+ compositionP u v w = (pure (.) <*> u <*> v <*> w) =-= (u <*> (v <*> w))+ homomorphismP f x = (pure f <*> pure x) =-= (pure (f x) :: m b)+ interchangeP u y = (u <*> pure y) =-= (pure ($ y) <*> u)+ functorP f x = (fmap f x) =-= (pure f <*> x)+++-- | 'Applicative' morphism properties+applicativeMorphism :: forall f g.+ ( Applicative f, Applicative g+ , Show (f NumT), Arbitrary (f NumT)+ , EqProp (g NumT), EqProp (g T)+ , Show (f (NumT -> T))+ , Arbitrary (f (NumT -> T))+ ) =>+ (forall a. f a -> g a) -> TestBatch+applicativeMorphism q =+ ( "applicative morphism"+ , [("pure", property pureP), ("apply", property applyP)] )+ where+ pureP :: NumT -> Property+ applyP :: f (NumT->T) -> f NumT -> Property+ + pureP a = q (pure a) =-= pure a+ applyP mf mx = q (mf <*> mx) =-= (q mf <*> q mx)+++semanticApplicative :: forall f g.+ ( Model1 f g+ , Applicative f, Applicative g+ , Arbitrary (f NumT), Arbitrary (f (NumT -> T))+ , EqProp (g NumT), EqProp (g T)+ , Show (f NumT), Show (f (NumT -> T))+ ) =>+ f () -> TestBatch+semanticApplicative =+ const (applicativeMorphism (model1 :: forall b. f b -> g b))+++-- | Properties to check that the 'Monad' @m@ satisfies the monad properties+monad :: forall m a b c.+ ( Monad m+ , Show a, Arbitrary a, Arbitrary b+ , Arbitrary (m a), EqProp (m a), Show (m a)+ , Arbitrary (m b), EqProp (m b)+ , Arbitrary (m c), EqProp (m c)+ ) =>+ m (a,b,c) -> TestBatch+monad = const ( "monad laws"+ , [ ("left identity", property leftP)+ , ("right identity", property rightP)+ , ("associativity" , property assocP)+ ]+ )+ where+ leftP :: (a -> m b) -> a -> Property+ rightP :: m a -> Property+ assocP :: m a -> (a -> m b) -> (b -> m c) -> Property+ + leftP f a = (return a >>= f) =-= f a+ rightP m = (m >>= return) =-= m+ assocP m f g = ((m >>= f) >>= g) =-= (m >>= (\x -> f x >>= g))++-- | Law for monads that are also instances of 'Functor'.+monadFunctor :: forall m a b.+ ( Functor m, Monad m+ , Arbitrary a, Arbitrary b+ , Arbitrary (m a), Show (m a), EqProp (m b)) =>+ m (a, b) -> TestBatch+monadFunctor = const ( "monad functor"+ , [("bind return", property bindReturnP)])+ where+ bindReturnP :: (a -> b) -> m a -> Property+ bindReturnP f xs = fmap f xs =-= (xs >>= return . f)++monadApplicative :: forall m a b.+ ( Applicative m, Monad m+ , EqProp (m a), EqProp (m b)+ , Show a, Arbitrary a+ , Show (m a), Arbitrary (m a)+ , Show (m (a -> b)), Arbitrary (m (a -> b))) =>+ m (a, b) -> TestBatch+monadApplicative = const ( "monad applicative"+ , [ ("pure", property pureP)+ , ("ap", property apP)+ ]+ )+ where+ pureP :: a -> Property+ apP :: m (a -> b) -> m a -> Property++ pureP x = (pure x :: m a) =-= return x+ apP f x = (f <*> x) =-= (f `ap` x)++-- | 'Monad' morphism properties++-- | 'Applicative' morphism properties+monadMorphism :: forall f g.+ ( Monad f, Monad g, Functor g+ , Show (f NumT)+ , Show (f (NumT -> T))+ , Show (f (f (NumT -> T)))+ , Arbitrary (f NumT), Arbitrary (f T)+ , Arbitrary (f (NumT -> T))+ , Arbitrary (f (f (NumT -> T)))+ , EqProp (g NumT), EqProp (g T)+ , EqProp (g (NumT -> T))+ ) =>+ (forall a. f a -> g a) -> TestBatch+monadMorphism q =+ ( "monad morphism"+ , [ ("return", property returnP), ("bind", property bindP), ("join", property joinP) ] )+ where+ returnP :: NumT -> Property+ bindP :: f NumT -> (NumT -> f T) -> Property+ joinP :: f (f (NumT->T)) -> Property+ + returnP a = q (return a) =-= return a+ bindP u k = q (u >>= k) =-= (q u >>= q . k)+ joinP uu = q (join uu) =-= join (fmap q (q uu))++-- The join and bind properties are redundant. Pick one.++-- q (join uu)+-- == q (uu >>= id)+-- == q uu >>= q . id+-- == q uu >>= q+-- == join (fmap q (q uu))++-- q (u >>= k)+-- == q (fmap k (join u))+-- == fmap k (q (join u)) -- if also a functor morphism+-- == fmap k (join (fmap q (q uu)))+-- == fmap k (q u >>= q)+-- == ???++-- I'm stuck at the end here. What's missing?++semanticMonad :: forall f g.+ ( Model1 f g+ , Monad f, Monad g+ , EqProp (g T) , EqProp (g NumT)+ , EqProp (g (NumT -> T))+ , Arbitrary (f T) , Arbitrary (f NumT)+ , Arbitrary (f (f (NumT -> T)))+ , Arbitrary (f (NumT -> T))+ , Show (f (f (NumT -> T)))+ , Show (f (NumT -> T)) , Show (f NumT)+ , Functor g+ ) =>+ f () -> TestBatch+semanticMonad = const (monadMorphism (model1 :: forall b. f b -> g b))++-- | Laws for MonadPlus instances with left distribution.+monadPlus :: forall m a b.+ ( MonadPlus m, Show (m a)+ , Arbitrary a, Arbitrary (m a), Arbitrary (m b)+ , EqProp (m a), EqProp (m b)) =>+ m (a, b) -> TestBatch+monadPlus = const ( "MonadPlus laws"+ , [ ("left zero", property leftZeroP)+ , ("left identity", leftId mplus (mzero :: m a))+ , ("right identity", rightId mplus (mzero :: m a))+ , ("associativity" , isAssoc (mplus :: Binop (m a)))+ , ("left distribution", property leftDistP)+ ]+ )+ where+ leftZeroP :: (a -> m b) -> Property+ leftDistP :: m a -> m a -> (a -> m b) -> Property++ leftZeroP k = (mzero >>= k) =-= mzero+ leftDistP a b k = (a `mplus` b >>= k) =-= ((a >>= k) `mplus` (b >>= k))++-- | Laws for MonadPlus instances with left catch.+monadOr :: forall m a b.+ ( MonadPlus m, Show a, Show (m a)+ , Arbitrary a, Arbitrary (m a), Arbitrary (m b)+ , EqProp (m a), EqProp (m b)) =>+ m (a, b) -> TestBatch+monadOr = const ( "MonadOr laws"+ , [ ("left zero", property leftZeroP)+ , ("left identity", leftId mplus (mzero :: m a))+ , ("right identity", rightId mplus (mzero :: m a))+ , ("associativity" , isAssoc (mplus :: Binop (m a)))+ , ("left catch", property leftCatchP)+ ]+ )+ where+ leftZeroP :: (a -> m b) -> Property+ leftCatchP :: a -> m a -> Property++ leftZeroP k = (mzero >>= k) =-= mzero+ leftCatchP a b = return a `mplus` b =-= return a+++arrow :: forall (~>) b c d e.+ ( Arrow (~>)+ , Show (d ~> e), Show (c ~> d), Show (b ~> c)+ , Show b, Show c, Show d, Show e+ , Arbitrary (d ~> e), Arbitrary (c ~> d), Arbitrary (b ~> c)+ , Arbitrary b, Arbitrary c, Arbitrary d, Arbitrary e+ , EqProp (b ~> e), EqProp (b ~> d)+ , EqProp ((b,d) ~> c)+ , EqProp ((b,d) ~> (c,d)), EqProp ((b,e) ~> (d,e))+ , EqProp ((b,d) ~> (c,e))+ , EqProp b, EqProp c, EqProp d, EqProp e+ ) =>+ b ~> (c,d,e) -> TestBatch+arrow = const ("arrow laws"+ , [ ("associativity" , property assocP)+ , ("arr distributes" , property arrDistributesP)+-- TODO: how to define h is onto or one-to-one?+-- , ("extensionality principle" , property extensionalityP)+-- , ("extensionality dual" , property extensionalityDualP)+ , ("first works as funs" , property firstAsFunP)+ , ("first keeps composition", property firstKeepCompP)+ , ("first works as fst" , property firstIsFstP)+ , ("second can move" , property secondMovesP)+ ]+ )+ where+ assocP :: b ~> c -> c ~> d -> d ~> e -> Property+ assocP f g h = ((f >>> g) >>> h) =-= (f >>> (g >>> h))+ + arrDistributesP :: (b -> c) -> (c -> d) -> Property+ arrDistributesP f g = ((arr (f >>> g)) :: b ~> d) =-= (arr f >>> arr g)+ + firstAsFunP :: (b -> c) -> Property+ firstAsFunP f = (first (arr f) :: (b,d) ~> (c,d)) =-= arr (first f)++ firstKeepCompP :: b ~> c -> c ~> d -> Property+ firstKeepCompP f g =+ ((first (f >>> g)) :: ((b,e) ~> (d,e))) =-= (first f >>> first g)+ + firstIsFstP :: b ~> c -> Property+ firstIsFstP f = ((first f :: (b,d) ~> (c,d)) >>> arr fst)+ =-= (arr fst >>> f)+ + secondMovesP :: (b ~> c) -> (d -> e) -> Property+ secondMovesP f g = (first f >>> second (arr g))+ =-= ((second (arr g)) >>> first f)++arrowChoice :: forall (~>) b c d e.+ ( ArrowChoice (~>)+ , Show (b ~> c)+ , Arbitrary (b ~> c)+ , Arbitrary b, Arbitrary c, Arbitrary d, Arbitrary e+ , EqProp ((Either b d) ~> (Either c e))+ , EqProp ((Either b d) ~> (Either c d))+ ) =>+ b ~> (c,d,e) -> TestBatch+arrowChoice = const ("arrow choice laws"+ , [ ("left works as funs" , property leftAsFunP)+ , ("right can move" , property rightMovesP)+ ]+ )+ where+ leftAsFunP :: (b -> c) -> Property+ leftAsFunP f = (left (arr f) :: (Either b d) ~> (Either c d))+ =-= arr (left f)++ rightMovesP :: (b ~> c) -> (d -> e) -> Property+ rightMovesP f g = (left f >>> right (arr g))+ =-= ((right (arr g)) >>> left f)++traversable :: forall f a b m.+ ( Traversable f, Monoid m, Show (f a)+ , Arbitrary (f a), Arbitrary b, Arbitrary a, Arbitrary m+ , EqProp (f b), EqProp m) =>+ f (a, b, m) -> TestBatch+traversable = const ( "traversable"+ , [ ("fmap", property fmapP)+ , ("foldMap", property foldMapP)+ ]+ )+ where+ fmapP :: (a -> b) -> f a -> Property+ foldMapP :: (a -> m) -> f a -> Property++ fmapP f x = f `fmap` x =-= f `fmapDefault` x+ foldMapP f x = f `foldMap` x =-= f `foldMapDefault` x
+ src/Test/QuickCheck/Instances.hs view
@@ -0,0 +1,20 @@+{-# OPTIONS_GHC -Wall -fno-warn-orphans #-}+module Test.QuickCheck.Instances+ (module Test.QuickCheck.Instances.Char+ ,module Test.QuickCheck.Instances.Eq+ ,module Test.QuickCheck.Instances.List+ ,module Test.QuickCheck.Instances.Num+ ,module Test.QuickCheck.Instances.Ord+ ,module Test.QuickCheck.Instances.Tuple+ ) where+ +import Test.QuickCheck.Instances.Array ()+import Test.QuickCheck.Instances.Char +import Test.QuickCheck.Instances.Eq+import Test.QuickCheck.Instances.Int ()+import Test.QuickCheck.Instances.List+import Test.QuickCheck.Instances.Maybe ()+import Test.QuickCheck.Instances.Num+import Test.QuickCheck.Instances.Ord+import Test.QuickCheck.Instances.Tuple+import Test.QuickCheck.Instances.Word ()
+ src/Test/QuickCheck/Instances/Array.hs view
@@ -0,0 +1,10 @@+module Test.QuickCheck.Instances.Array where++import Test.QuickCheck+import Control.Applicative+import Data.Array++instance (Ix a, Integral a, Arbitrary b) => Arbitrary (Array a b) where+ arbitrary =+ (\x -> listArray (0,fromIntegral (length x - 1)) x) <$> arbitrary + coarbitrary = coarbitrary . elems
+ src/Test/QuickCheck/Instances/Char.hs view
@@ -0,0 +1,75 @@+module Test.QuickCheck.Instances.Char+ (nonSpace,whitespace,space,newline+ ,lowerAlpha,upperAlpha,numeric+ ,parenthesis,bracket,brace+ ,operator+ ) where++import Data.Char+import Test.QuickCheck+import Test.QuickCheck.Instances.Eq++instance Arbitrary Char where+ arbitrary = choose ('\0','\255')+ coarbitrary = variant . ord++-- Bob: why the `rem` 4 ?++{- | Generates a 'non space' character, i.e. any ascii except+ ' ', '\t', '\n' and '\r'.+-}+nonSpace :: Gen Char+nonSpace = notOneof " \t\n\r"++{- | Generates any whitespace character, including new lines.+-}+whitespace :: Gen Char+whitespace = oneof [space,newline]++{- | Generates a whitespace charecter, not a newline.+-}+space :: Gen Char+space = oneof (map return " \t")++{- | Generates either a '\n' or '\r'.+-}+newline :: Gen Char+newline = oneof (map return "\n\r")++letters :: String+letters = "abcdefghijklmnopqrstuvwxyz"++{- | Generates any lower case alpha character.+-}+lowerAlpha :: Gen Char+lowerAlpha = oneof (map return letters)++{- | Generates any upper case alpha character.+-}+upperAlpha :: Gen Char+upperAlpha = oneof (map (return . toUpper) letters)++{- | Generates a digit character.+-}+numeric :: Gen Char+numeric = oneof (map return "1234567890")++{- | Generates one or other of '(' and ')'.+-}+parenthesis :: Gen Char+parenthesis = oneof (map return "()")++{- | Generates one or other of '[' and ']'.+-}+bracket :: Gen Char+bracket = oneof (map return "[]")++{- | Generates one or other of '{' and '}'.+-}+brace :: Gen Char+brace = oneof (map return "{}")++{- | Generates one of '*', '/', '-', '+', '<', '>', '|' and '#'.+-}+operator :: Gen Char+operator = oneof (map return "*/-+<>|#")
+ src/Test/QuickCheck/Instances/Eq.hs view
@@ -0,0 +1,12 @@+module Test.QuickCheck.Instances.Eq (notEqualTo, notOneof) where++import Test.QuickCheck+import Test.QuickCheck.Checkers+import Control.Monad.Extensions++notEqualTo :: (Eq a,Arbitrary a) => a -> Gen a -> Gen a+notEqualTo v = satisfiesM (/= v)++notOneof :: (Eq a,Arbitrary a) => [a] -> Gen a+notOneof es = arbitrarySatisfying (not . (`elem` es))+
+ src/Test/QuickCheck/Instances/Int.hs view
@@ -0,0 +1,21 @@+module Test.QuickCheck.Instances.Int where++import Control.Applicative+import Test.QuickCheck+import Data.Int++instance Arbitrary Int64 where+ arbitrary = fromInteger <$> arbitrary+ coarbitrary = variant . fromIntegral++instance Arbitrary Int32 where+ arbitrary = fromInteger <$> arbitrary+ coarbitrary = variant . fromIntegral++instance Arbitrary Int16 where+ arbitrary = fromInteger <$> arbitrary+ coarbitrary = variant . fromIntegral++instance Arbitrary Int8 where+ arbitrary = fromInteger <$> arbitrary+ coarbitrary = variant . fromIntegral
+ src/Test/QuickCheck/Instances/List.hs view
@@ -0,0 +1,72 @@+module Test.QuickCheck.Instances.List+ (anyList,nonEmpty+ ,infiniteList+ ,increasing,nondecreasing+ ,increasingInf,nondecreasingInf+ ,decreasing,nonincreasing+ ,decreasingInf,nonincreasingInf+ ) where++import Test.QuickCheck+import Test.QuickCheck.Applicative ()+import Test.QuickCheck.Instances.Num+import Control.Applicative++{- | Generates a non-empty list with the contents generated using its+ argument.+-}+nonEmpty :: Gen a -> Gen [a]+nonEmpty x = liftA2 (:) x (anyList x)++{- | Generates any list (possibly empty) with the contents generated using+ its argument.+-}+anyList :: Gen a -> Gen [a]+anyList x = frequency [(1, pure []), (4, nonEmpty x)]++{- | Generates an infinite list with contents generated using its argument+-}+infiniteList :: Gen a -> Gen [a]+infiniteList x = liftA2 (:) x (infiniteList x)++sumA :: (Applicative f, Num a) => f a -> f [a] -> f [a]+sumA = liftA2 (scanl (+))++monotonic_ :: (Arbitrary a, Num a) => (Gen a -> Gen [a]) -> Gen a -> Gen [a]+monotonic_ listGen gen = sumA arbitrary (listGen gen)++-- TODO: Generalise this to Ord a.+monotonic :: (Arbitrary a, Num a) => Gen a -> Gen [a]+monotonic gen = monotonic_ anyList gen++-- | Generate increasing towards infinity+increasing :: (Arbitrary a, Num a) => Gen [a]+increasing = monotonic positive++-- | Generate an infinite list of increasing values+increasingInf :: (Arbitrary a, Num a) => Gen [a]+increasingInf = monotonic_ infiniteList positive++-- | Generate nondecreasing values+nondecreasing :: (Arbitrary a, Num a) => Gen [a]+nondecreasing = monotonic nonNegative++-- | Generate an infinite list of nondecreasing values+nondecreasingInf :: (Arbitrary a, Num a) => Gen [a]+nondecreasingInf = monotonic_ infiniteList nonNegative++-- | Generate increasing towards infinity+decreasing :: (Arbitrary a, Num a) => Gen [a]+decreasing = monotonic negative++-- | Generate an infinite list of increasing values+decreasingInf :: (Arbitrary a, Num a) => Gen [a]+decreasingInf = monotonic_ infiniteList negative++-- | Generate nondecreasing values+nonincreasing :: (Arbitrary a, Num a) => Gen [a]+nonincreasing = monotonic nonPositive++-- | Generate an infinite list of nondecreasing values+nonincreasingInf :: (Arbitrary a, Num a) => Gen [a]+nonincreasingInf = monotonic_ infiniteList nonPositive
+ src/Test/QuickCheck/Instances/Maybe.hs view
@@ -0,0 +1,9 @@+module Test.QuickCheck.Instances.Maybe where++import Test.QuickCheck+import Test.QuickCheck.Applicative ()+import Control.Applicative++maybeGen :: Gen a -> Gen (Maybe a)+maybeGen x = oneof [pure Nothing+ ,Just <$> x]
+ src/Test/QuickCheck/Instances/Num.hs view
@@ -0,0 +1,28 @@+module Test.QuickCheck.Instances.Num + (nonNegative,nonPositive+ ,negative,positive+ ,nonZero,nonZero_+ ) where++import Test.QuickCheck+import Control.Monad.Extensions+import Control.Applicative++nonNegative :: (Num a, Arbitrary a) => Gen a+nonNegative = abs <$> arbitrary++positive :: (Num a, Arbitrary a) => Gen a+positive = nonZero nonNegative++nonPositive :: (Num a, Arbitrary a) => Gen a+nonPositive = negate <$> nonNegative++negative :: (Num a, Arbitrary a) => Gen a+negative = negate <$> positive++nonZero :: (Num a, Arbitrary a) => Gen a -> Gen a+nonZero g =+ sized (\s -> satisfiesM (/= 0) (if (s == 0) then (resize 1 g) else g))++nonZero_ :: (Num a, Arbitrary a) => Gen a+nonZero_ = nonZero arbitrary
+ src/Test/QuickCheck/Instances/Ord.hs view
@@ -0,0 +1,10 @@+module Test.QuickCheck.Instances.Ord where++import Test.QuickCheck+import Control.Monad.Extensions++greaterThan :: (Ord a,Arbitrary a) => a -> Gen a -> Gen a+greaterThan v = satisfiesM (> v)++lessThan :: (Ord a,Arbitrary a) => a -> Gen a -> Gen a+lessThan v = satisfiesM (< v)
+ src/Test/QuickCheck/Instances/Tuple.hs view
@@ -0,0 +1,25 @@+module Test.QuickCheck.Instances.Tuple where++import Test.QuickCheck+import Control.Monad++{- | Generates a 2-tuple using its arguments to generate the parts.+-}+(>*<) :: Gen a -> Gen b -> Gen (a,b)+x >*< y = liftM2 (,) x y++{- | Generates a 3-tuple using its arguments to generate the parts.+-}+(>**<) :: Gen a -> Gen b -> Gen c -> Gen (a,b,c)+(>**<) x y z = liftM3 (,,) x y z++{- | Generates a 4-tuple using its arguments to generate the parts.+-}+(>***<) :: Gen a -> Gen b -> Gen c -> Gen d -> Gen (a,b,c,d)+(>***<) x y z a = liftM4 (,,,) x y z a++{- | Generates a 5-tuple using its arguments to generate the parts.+-}+(>****<) :: Gen a -> Gen b -> Gen c -> Gen d -> Gen e -> Gen (a,b,c,d,e)+(>****<) x y z a b= liftM5 (,,,,) x y z a b+
+ src/Test/QuickCheck/Instances/Word.hs view
@@ -0,0 +1,21 @@+module Test.QuickCheck.Instances.Word where++import Control.Applicative+import Test.QuickCheck+import Data.Word++instance Arbitrary Word64 where+ arbitrary = fromInteger <$> arbitrary+ coarbitrary = variant . fromIntegral++instance Arbitrary Word32 where+ arbitrary = fromInteger <$> arbitrary+ coarbitrary = variant . fromIntegral++instance Arbitrary Word16 where+ arbitrary = fromInteger <$> arbitrary+ coarbitrary = variant . fromIntegral++instance Arbitrary Word8 where+ arbitrary = fromInteger <$> arbitrary+ coarbitrary = variant . fromIntegral
+ src/Test/QuickCheck/Later.hs view
@@ -0,0 +1,82 @@+{-# OPTIONS_GHC -Wall #-}+----------------------------------------------------------------------+-- |+-- Module : Data.Later+-- Copyright : (c) David Sankel 2008+-- License : BSD3+-- +-- Maintainer : david@sankelsoftware.com+-- Stability : experimental+-- +-- Later. Allows for testing of functions that depend on the order of+-- evaluation.+--+-- TODO: move this functionality to the testing package for Unamb.+----------------------------------------------------------------------++module Test.QuickCheck.Later+ ( isAssocTimes+ , isCommutTimes+ , delay+ , delayForever+ ) where++import Test.QuickCheck.Checkers+import Test.QuickCheck++import System.Random (Random)++import System.IO.Unsafe+import Control.Concurrent+import Control.Monad (forever)++-- Generate a random delay up to given max seconds for a property.+delayP :: (Num t, System.Random.Random t, Testable b) => t -> (t -> b) -> Property+delayP d = forAll (genR (0,d))++-- | Is the given function commutative when restricted to the same value+-- but possibly different times?+isCommutTimes :: (EqProp b, Arbitrary a, Show a) => Double -> (a -> a -> b) -> Property++isCommutTimes d (#) =+ delayP d $ \ t1 ->+ delayP d $ \ t2 ->+ \ v -> let del = flip delay v in+ del t1 # del t2 =-= del t2 # del t1++-- Note that we delay v by t1 and by t2 twice.+-- +-- TODO: make sure CSE isn't kicking in. Examine the core code.++-- | Is the given function associative when restricted to the same value+-- but possibly different times?+isAssocTimes :: (EqProp a, Arbitrary a, Show a) => Double -> (a -> a -> a) -> Property++isAssocTimes d (#) =+ delayP d $ \ t1 ->+ delayP d $ \ t2 ->+ delayP d $ \ t3 ->+ \ v -> let del = flip delay v in+ (del t1 # del t2) # del t3 =-= del t1 # (del t2 # del t3)+++-- The value eventually returned by an action. Probably handy elsewhere.+-- TODO: what are the necessary preconditions in order to make this+-- function referentially transparent?+eventually :: IO a -> a+eventually = unsafePerformIO . unsafeInterleaveIO++-- Why unsafeInterleaveIO? Because ...++-- | Delay a value's availability by the given duration in seconds.+-- Note that the delay happens only on the first evaluation.+delay :: RealFrac t => t -> a -> a+delay d a = eventually $ threadDelay (round (1.0e6 * d)) >> return a++-- | A value that is never available. Rerun of @hang@ from unamb, but+-- replicated to avoid mutual dependency.+-- +-- TODO: Remove when this module is moved into the unamb-test package.+delayForever :: a+delayForever = unsafePerformIO $ do forever (threadDelay maxBound)+ return undefined
+ wikipage.tw view
@@ -0,0 +1,13 @@+[[Category:Packages]]++== Abstract ==++'''checkers''' is a library for reusable QuickCheck properties, particularly for standard type classes (class laws and [http://conal.net/papers/simply-reactive class morphisms]). For instance, most of [[Reactive]] can be specified and tested using just these properties. Checkers also lots of support for randomly generating data values (thanks to Thomas Davie).++Besides this wiki page, here are more ways to find out about checkers:+* Read [http://code.haskell.org/checkers/doc/html/ the library documentation].+* Get the code repository: '''<tt>darcs get http://code.haskell.org/checkers</tt>'''.+* Install from [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/checkers Hackage].+* See the [[checkers/Versions| version history]].++Please leave comments at the [[Talk:checkers|Talk page]].