quickspec 0.9.2 → 0.9.3
raw patch · 10 files changed
+95/−45 lines, 10 filesdep ~QuickCheck
Dependency ranges changed: QuickCheck
Files
- LICENSE +1/−1
- quickspec.cabal +2/−2
- src/Test/QuickSpec.hs +2/−2
- src/Test/QuickSpec/Approximate.hs +2/−1
- src/Test/QuickSpec/Generate.hs +31/−21
- src/Test/QuickSpec/Main.hs +10/−5
- src/Test/QuickSpec/Prelude.hs +4/−4
- src/Test/QuickSpec/Signature.hs +20/−1
- src/Test/QuickSpec/Term.hs +21/−7
- src/Test/QuickSpec/TestTotality.hs +2/−1
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2009-2012, Nick Smallbone+Copyright (c) 2009-2014, Nick Smallbone All rights reserved.
quickspec.cabal view
@@ -1,5 +1,5 @@ Name: quickspec-Version: 0.9.2+Version: 0.9.3 Cabal-version: >=1.6 Build-type: Simple @@ -90,5 +90,5 @@ Test.QuickSpec.Utils.MemoValuation Build-depends:- base < 5, containers, transformers, QuickCheck,+ base < 5, containers, transformers, QuickCheck >= 2.7, random, spoon >= 0.2, array, ghc-prim, mtl
src/Test/QuickSpec.hs view
@@ -33,14 +33,14 @@ -- If you want to get equations over a type that isn't in `Ord`, -- you must use the `observerN` family of functions (below) -- to define an observation function for that type.- con, fun0, fun1, fun2, fun3, fun4,+ con, fun0, fun1, fun2, fun3, fun4, fun5, -- * Adding functions whose results are not in `Ord` -- -- | These functions work the same as `funN` (above), -- but don't use `Ord` to compare the results of the functions. -- Instead you can use the `observerN` family of functions (below) -- to define an observation function.- blind0, blind1, blind2, blind3, blind4,+ blind0, blind1, blind2, blind3, blind4, blind5, -- * Adding variables to a signature vars, gvars,
src/Test/QuickSpec/Approximate.hs view
@@ -4,6 +4,7 @@ import Test.QuickCheck import Test.QuickCheck.Gen+import Test.QuickCheck.Random import Test.QuickSpec.Signature import Test.QuickSpec.Term import Test.QuickSpec.Utils@@ -36,7 +37,7 @@ unlifted [] = return [] unlifted (x:xs) = liftM2 (:) (lifted x) (lifted xs) -approximate :: Partial a => (forall a. Partial a => a -> Maybe a) -> StdGen -> Int -> a -> a+approximate :: Partial a => (forall a. Partial a => a -> Maybe a) -> QCGen -> Int -> a -> a approximate eval g n x = unGen (runReaderT (lifted x) (Plug plug)) g n where plug :: forall a. Partial a => Gen a -> Gen a
src/Test/QuickSpec/Generate.hs view
@@ -16,36 +16,40 @@ import Text.Printf import Test.QuickSpec.Utils.Typeable import Test.QuickSpec.Utils-import Test.QuickCheck.Gen+import Test.QuickCheck.Gen hiding (generate)+import Test.QuickCheck.Random import System.Random import Control.Spoon import Test.QuickSpec.Utils.MemoValuation terms :: Sig -> TypeRel Expr -> TypeRel Expr-terms sig base =+terms = termsSatisfying (const True)++termsSatisfying :: (Term -> Bool) -> Sig -> TypeRel Expr -> TypeRel Expr+termsSatisfying p sig base = TypeMap.fromList- [ Some (O (terms' sig base w))+ [ Some (O (terms' p sig base w)) | Some (Witness w) <- usort (saturatedTypes sig ++ variableTypes sig) ] -terms' :: Typeable a => Sig -> TypeRel Expr -> a -> [Expr a]-terms' sig base w =- filter (\t -> size 1 (term t) <= maxSize sig) $+terms' :: Typeable a => (Term -> Bool) -> Sig -> TypeRel Expr -> a -> [Expr a]+terms' p sig base w =+ filter (\t -> size 1 (term t) <= maxSize sig && p (term t)) $ map var (TypeRel.lookup w (variables sig)) ++ map con (TypeRel.lookup w (constants sig)) ++ [ app f x | Some (Witness w') <- lhsWitnesses sig w, x <- TypeRel.lookup w' base, not (isUndefined (term x)),- f <- terms' sig base (const w),+ f <- terms' p sig base (const w), arity f > 0, not (isUndefined (term f)) ] -test :: [(Valuation, StdGen, Int)] -> Sig ->+test :: [(Valuation, QCGen, Int)] -> Sig -> TypeMap (List `O` Expr) -> TypeMap (TestResults `O` Expr) test vals sig ts = fmap (mapSome2 (test' vals sig)) ts test' :: forall a. Typeable a =>- [(Valuation, StdGen, Int)] -> Sig -> [Expr a] -> TestResults (Expr a)+ [(Valuation, QCGen, Int)] -> Sig -> [Expr a] -> TestResults (Expr a) test' vals sig ts | not (testable sig (undefined :: a)) = discrete ts | otherwise =@@ -58,33 +62,39 @@ base = minTests sig `div` 2 increment = minTests sig - base -genSeeds :: Int -> IO [(StdGen, Int)]+genSeeds :: Int -> IO [(QCGen, Int)] genSeeds maxSize = do- rnd <- newStdGen+ rnd <- newQCGen let rnds rnd = rnd1 : rnds rnd2 where (rnd1, rnd2) = split rnd return (zip (rnds rnd) (concat (repeat [0,2..maxSize]))) -toValuation :: Strategy -> Sig -> (StdGen, Int) -> (Valuation, StdGen, Int)+toValuation :: Strategy -> Sig -> (QCGen, Int) -> (Valuation, QCGen, Int) toValuation strat sig (g, n) = let (g1, g2) = split g in (memoValuation sig (unGen (valuation strat) g1 n), g2, n) -generate :: Strategy -> Sig -> IO (TypeMap (TestResults `O` Expr))-generate strat sig | maxDepth sig < 0 =+generate :: Bool -> Strategy -> Sig -> IO (TypeMap (TestResults `O` Expr))+generate shutUp strat sig = generateTermsSatisfying shutUp (const True) strat sig++generateTermsSatisfying :: Bool -> (Term -> Bool) -> Strategy -> Sig -> IO (TypeMap (TestResults `O` Expr))+generateTermsSatisfying shutUp p strat sig | maxDepth sig < 0 = ERROR "generate: maxDepth must be positive"-generate strat sig | maxDepth sig == 0 = return TypeMap.empty-generate strat sig = unbuffered $ do+generateTermsSatisfying shutUp p strat sig | maxDepth sig == 0 = return TypeMap.empty+generateTermsSatisfying shutUp p strat sig = unbuffered $ do let d = maxDepth sig- rs <- fmap (TypeMap.mapValues2 reps) (generate (const partialGen) (updateDepth (d-1) sig))- printf "Depth %d: " d+ quietly x | shutUp = return ()+ | otherwise = x+ rs <- fmap (TypeMap.mapValues2 reps) (generate shutUp (const partialGen) (updateDepth (d-1) sig))+ quietly $ printf "Depth %d: " d let count :: ([a] -> a) -> (forall b. f (g b) -> a) -> TypeMap (f `O` g) -> a count op f = op . map (some2 f) . TypeMap.toList- ts = terms sig rs- printf "%d terms, " (count sum length ts)+ ts = termsSatisfying p sig rs+ quietly $ printf "%d terms, " (count sum length ts) seeds <- genSeeds (maxQuickCheckSize sig) let cs = test (map (toValuation strat sig) seeds) sig ts- printf "%d tests, %d evaluations, %d classes, %d raw equations.\n"+ quietly $+ printf "%d tests, %d evaluations, %d classes, %d raw equations.\n" (count (maximum . (0:)) numTests cs) (count sum numResults cs) (count sum (length . classes) cs)
src/Test/QuickSpec/Main.hs view
@@ -102,21 +102,26 @@ quickSpec :: Signature a => a -> IO () quickSpec = runTool $ \sig -> do putStrLn "== Testing =="- r <- generate (const partialGen) sig+ r <- generate False (const partialGen) sig let clss = concatMap (some2 (map (Some . O) . classes)) (TypeMap.toList r)+ univ = concatMap (some2 (map (tagged term))) clss reps = map (some2 (tagged term . head)) clss eqs = equations clss printf "%d raw equations; %d terms in universe.\n\n" (length eqs) (length reps) - let ctx = initial (maxDepth sig) (symbols sig) reps+ let ctx = initial (maxDepth sig) (symbols sig) univ allEqs = map (some eraseEquation) eqs isBackground = all silent . eqnFuns+ keep eq = not (isBackground eq) || absurd eq+ absurd (t :=: u) = absurd1 t u || absurd1 u t+ absurd1 (Var x) t = x `notElem` vars t+ absurd1 _ _ = False (background, foreground) = partition isBackground allEqs- pruned = filter (not . isBackground)- (prune ctx (map erase reps) id+ pruned = filter keep+ (prune ctx (filter (not . isUndefined) (map erase reps)) id (background ++ foreground)) eqnFuns (t :=: u) = funs t ++ funs u isGround (t :=: u) = null (vars t) && null (vars u)@@ -146,7 +151,7 @@ sampleTerms :: Signature a => a -> IO () sampleTerms = runTool $ \sig -> do putStrLn "== Testing =="- r <- generate (const partialGen) (updateDepth (maxDepth sig - 1) sig)+ r <- generate False (const partialGen) (updateDepth (maxDepth sig - 1) sig) let univ = sort . concatMap (some2 (map term)) . TypeMap.toList . terms sig . TypeMap.mapValues2 reps $ r printf "Universe contains %d terms.\n\n" (length univ)
src/Test/QuickSpec/Prelude.hs view
@@ -12,14 +12,14 @@ -- | Just a type. -- You can instantiate your polymorphic functions at this type -- to include them in a signature.-newtype A = A Int deriving (Eq, Ord, Typeable, Arbitrary, CoArbitrary, Partial)-newtype B = B Int deriving (Eq, Ord, Typeable, Arbitrary, CoArbitrary, Partial)-newtype C = C Int deriving (Eq, Ord, Typeable, Arbitrary, CoArbitrary, Partial)+newtype A = A Int deriving (Eq, Ord, Typeable, Arbitrary, CoArbitrary, Partial, Show)+newtype B = B Int deriving (Eq, Ord, Typeable, Arbitrary, CoArbitrary, Partial, Show)+newtype C = C Int deriving (Eq, Ord, Typeable, Arbitrary, CoArbitrary, Partial, Show) -- | A type with two elements. -- Use this instead of @A@ if testing doesn't work well because -- the domain of @A@ is too large.-data Two = One | Two deriving (Eq, Ord, Typeable)+data Two = One | Two deriving (Eq, Ord, Typeable, Show) instance Arbitrary Two where arbitrary = elements [One, Two]
src/Test/QuickSpec/Signature.hs view
@@ -324,6 +324,12 @@ `mappend` typeSig (undefined :: d) `mappend` typeSig (undefined :: e) +primCon5 :: forall a b c d e f. (Typeable a, Typeable b, Typeable c, Typeable d, Typeable e, Typeable f) =>+ Int -> String -> (a -> b -> c -> d -> e -> f) -> Sig+primCon5 n x f = primCon4 n x f+ `mappend` typeSig (undefined :: e)+ `mappend` typeSig (undefined :: f)+ -- | A constant. blind0 :: forall a. Typeable a => String -> a -> Sig blind0 = primCon0 0@@ -343,6 +349,10 @@ blind4 :: forall a b c d e. (Typeable a, Typeable b, Typeable c, Typeable d, Typeable e) => String -> (a -> b -> c -> d -> e) -> Sig blind4 = primCon4 4+-- | A function of arity 5.+blind5 :: forall a b c d e f. (Typeable a, Typeable b, Typeable c, Typeable d, Typeable e, Typeable f) =>+ String -> (a -> b -> c -> d -> e -> f) -> Sig+blind5 = primCon5 5 ord :: (Ord a, Typeable a) => a -> Sig ord x = ordSig (Observer (pgen (return id)) `observing` x)@@ -441,6 +451,13 @@ fun4 x f = blind4 x f `mappend` ord (f undefined undefined undefined undefined) +-- | A function of five arguments.+fun5 :: (Typeable a, Typeable b, Typeable c, Typeable d,+ Typeable e, Typeable f, Ord f) =>+ String -> (a -> b -> c -> d -> e -> f) -> Sig+fun5 x f = blind5 x f+ `mappend` ord (f undefined undefined undefined undefined undefined)+ -- | An observation function of arity 1. observer1 :: (Typeable a, Typeable b, Ord b) => (a -> b) -> Sig observer1 f = observerSig (Observer (pgen (return f)))@@ -551,13 +568,15 @@ where next = head (filter (`notElem` used) candidates) candidates | null wellTypedNames = ERROR "null allVars"- | otherwise = wellTypedNames ++ concat [ map (++ show i) wellTypedNames | i <- [1.. ] ]+ | otherwise = concat [ map (++ suffix) wellTypedNames | suffix <- suffixes ] allVars = map (some (sym . unVariable)) (TypeRel.toList (variables sig)) ++ ss wellTypedNames = [ name v | v <- allVars, symbolType v == symbolType x ]+ suffixes =+ concat ([sequence (replicate n ['a'..'z']) | n <- [0..]]) constantSymbols, variableSymbols, symbols :: Sig -> [Symbol] constantSymbols sig =
src/Test/QuickSpec/Term.hs view
@@ -7,9 +7,11 @@ import Test.QuickSpec.Utils.Typeable import Test.QuickCheck import Test.QuickCheck.Gen+import Test.QuickCheck.Gen.Unsafe import Data.Function import Data.Ord import Data.Char+import Data.List import Test.QuickSpec.Utils data Symbol = Symbol {@@ -51,7 +53,7 @@ body (App f x) = Right (f, x) instance Show Term where- showsPrec p t = showString (showTerm p t)+ showsPrec p t = showString (showTerm p (hideImplicit t)) where brack s = "(" ++ s ++ ")" parenFun p s | p < 2 = s@@ -69,6 +71,14 @@ showTerm p (f `App` x) = parenFun p (showTerm 1 f ++ " " ++ showTerm 2 x) + hideImplicit (f `App` x)+ | isImplicit x = f+ | otherwise = hideImplicit f `App` hideImplicit x+ hideImplicit t = t++ isImplicit (Var v) | "_" `isPrefixOf` name v = True+ isImplicit _ = False+ showOp :: String -> String showOp op | isOp op = "(" ++ op ++ ")" | otherwise = op@@ -131,6 +141,11 @@ mapVars f (Const x) = Const x mapVars f (App t u) = App (mapVars f t) (mapVars f u) +mapConsts :: (Symbol -> Symbol) -> Term -> Term+mapConsts f (Var x) = Var x+mapConsts f (Const x) = Const (f x)+mapConsts f (App t u) = App (mapConsts f t) (mapConsts f u)+ data Expr a = Expr { term :: Term, arity :: {-# UNPACK #-} !Int,@@ -175,14 +190,13 @@ -- Generate a random variable valuation newtype Valuation = Valuation { unValuation :: forall a. Variable a -> a } -promoteVal :: (forall a. Gen (Variable a -> a)) -> Gen Valuation-promoteVal g = MkGen (\r n -> Valuation (unGen g r n))+promoteVal :: (forall a. Variable a -> Gen a) -> Gen Valuation+promoteVal g = do+ Capture eval <- capture+ return (Valuation (eval . g)) valuation :: Strategy -> Gen Valuation-valuation strat = promoteVal (promote (\(Variable x) -> index (sym x) `variant'` strat (sym x) (value x)))- where -- work around the fact that split doesn't work- variant' 0 = variant (0 :: Int)- variant' n = variant (-1 :: Int) . variant' (n-1)+valuation strat = promoteVal (\(Variable x) -> index (sym x) `variant` strat (sym x) (value x)) var :: Variable a -> Expr a var v@(Variable (Atom x _)) = Expr (Var x) (symbolArity x) (\env -> unValuation env v)
src/Test/QuickSpec/TestTotality.hs view
@@ -17,6 +17,7 @@ import Test.QuickSpec.Term hiding (symbols) import Test.QuickCheck import Test.QuickCheck.Gen+import Test.QuickCheck.Random import System.Random import Control.Monad import Data.List hiding (lookup)@@ -70,6 +71,6 @@ always :: Sig -> Gen Bool -> IO Bool always sig x = do- gens <- replicateM 100 newStdGen+ gens <- replicateM 100 newQCGen let sizes = cycle [0,2..maxQuickCheckSize sig] return (and [unGen x g n | (g, n) <- zip gens sizes])