packages feed

smartcheck 0.1 → 0.2

raw patch · 14 files changed

+663/−747 lines, 14 filesdep +lazysmallcheckdep +testing-featdep ~QuickCheckdep ~smartchecknew-component:exe:sc-qcPVP ok

version bump matches the API change (PVP)

Dependencies added: lazysmallcheck, testing-feat

Dependency ranges changed: QuickCheck, smartcheck

API changes (from Hackage documentation)

- Test.SmartCheck: (-->) :: Bool -> Bool -> ScProperty
- Test.SmartCheck: data ScProperty
- Test.SmartCheck: instance (Arbitrary a, Testable prop, Show a, Read a, ScProp prop) => ScProp (a -> prop)
- Test.SmartCheck: instance Eq ScProperty
- Test.SmartCheck: instance Read ScProperty
- Test.SmartCheck: instance ScProp Bool
- Test.SmartCheck: instance ScProp ScProperty
- Test.SmartCheck: instance Show ScProperty
- Test.SmartCheck: instance Testable ScProperty
- Test.SmartCheck: runQCInit :: (Show a, Read a, Arbitrary a, ScProp prop, Testable prop) => Args -> (a -> prop) -> IO (Maybe a, a -> Property)
- Test.SmartCheck.Args: qc :: ScArgs -> Bool
+ Test.SmartCheck: runQC :: (Show a, Arbitrary a, Testable prop) => Args -> (a -> prop) -> IO (Maybe a, a -> Property)
+ Test.SmartCheck: smartCheckInput :: (SubTypes a, Generic a, ConNames (Rep a), Testable prop, Read a) => ScArgs -> (a -> prop) -> IO ()
+ Test.SmartCheck.Test: scQuickCheckWithResult :: (Show a, Arbitrary a, Testable prop) => Args -> (a -> prop) -> IO (Maybe a, Result)
+ Test.SmartCheck.Test: stdArgs :: Args
+ Test.SmartCheck.Types: instance [overlap ok] Applicative Result
- Test.SmartCheck: smartCheck :: (Read a, Arbitrary a, SubTypes a, Generic a, ConNames (Rep a), ScProp prop, Testable prop) => ScArgs -> (a -> prop) -> IO ()
+ Test.SmartCheck: smartCheck :: (SubTypes a, Generic a, ConNames (Rep a), Testable prop) => ScArgs -> (a -> prop) -> IO ()
- Test.SmartCheck.Args: ScArgs :: Format -> Args -> Bool -> Int -> Maybe Int -> Int -> Bool -> Int -> Int -> Bool -> Int -> ScArgs
+ Test.SmartCheck.Args: ScArgs :: Format -> Args -> Int -> Maybe Int -> Int -> Bool -> Int -> Int -> Bool -> Int -> ScArgs

Files

− examples/Div0.hs
@@ -1,95 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}---- | Divide by 0 example in a simple arithmetic language.--module Div0 where--import Test.QuickCheck-import Test.SmartCheck-import Control.Monad--import GHC.Generics-import Data.Typeable---------------------------------------------------------------------data Exp = C Int-         | Add Exp Exp-         | Div Exp Exp-  deriving (Read, Show, Typeable, Generic)--instance SubTypes Exp--eval :: Exp -> Maybe Int-eval (C i) = Just i-eval (Add e0 e1) =-  liftM2 (+) (eval e0) (eval e1)-eval (Div e0 e1) =-  let e = eval e1 in-  if e == Just 0 then Nothing-    else liftM2 div (eval e0) e--instance Arbitrary Exp where-  arbitrary = sized mkM-    where-    mkM 0 = liftM C arbitrary-    mkM n = oneof [ liftM2 Add mkM' mkM'-                  , liftM2 Div mkM' mkM' ]-      where mkM' = mkM =<< choose (0,n-1)--  -- shrink (C i)       = map C (shrink i)-  -- shrink (Add e0 e1) = [e0, e1]-  -- shrink (Div e0 e1) = [e0, e1]---- property: so long as 0 isn't in the divisor, we won't try to divide by 0.--- It's false: something might evaluate to 0 still.-prop_div :: Exp -> ScProperty-prop_div e = divSubTerms e --> eval e /= Nothing--- prop_div e = property $ case x of---                           Nothing -> True---                           Just True -> True---                           _       -> False---   where x = fmap (< 1) (eval e)--  -- precondition: no dividand in a subterm can be 0.-divSubTerms :: Exp -> Bool-divSubTerms (C _)         = True-divSubTerms (Div _ (C 0)) = False-divSubTerms (Add e0 e1)   = divSubTerms e0 && divSubTerms e1-divSubTerms (Div e0 e1)   = divSubTerms e0 && divSubTerms e1---- div0 (A _ _) = property False--- div0 _       = property True---- prop_test m = case eval m of---                 Nothing -> True---                 Just i -> i < 5--divTest :: IO ()-divTest = smartCheck args prop_div-  where-  args = scStdArgs { qcArgs  = stdArgs-                                -- { maxSuccess = 1000-                                -- , maxSize    = 20  }-                   , format  = PrintString-                   , runForall  = True-                   }---- Get the minimal offending sub-value.-findVal :: Exp -> (Exp,Exp)-findVal (Div e0 e1)-  | eval e1 == Just 0     = (e0,e1)-  | eval e1 == Nothing    = findVal e1-  | otherwise             = findVal e0-findVal a@(Add e0 e1)-  | eval e0 == Nothing    = findVal e0-  | eval e1 == Nothing    = findVal e1-  | eval a == Just 0      = (a,a)-findVal _                 = error "not possible"--divSubValue :: Exp-divSubValue =-  Add (Div (C 5) (C (-12))) (Add (Add (C 2) (C 4)) (Add (C 7) (Div (C 3) (Add (C (-5)) (C 5)))))----------------------------------------------------------------------------------
− examples/Heap_Program.hs
@@ -1,227 +0,0 @@-{-# LANGUAGE ScopedTypeVariables, TemplateHaskell, DeriveDataTypeable, StandaloneDeriving #-}-{-# LANGUAGE DeriveGeneric #-}--{-# OPTIONS_GHC -fno-warn-missing-signatures #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}---- Copied from QuickCheck2's examples.--module Heap_Program where------------------------------------------------------------------------------- imports--import Test.QuickCheck-import Test.QuickCheck.Poly--import Data.List-  ( sort-  , (\\)-  )-import Data.Typeable--import GHC.Generics--import qualified Test.SmartCheck as SC------------------------------------------------------------------------------- SmartCheck Testing.  Comment out shrink instance if you want to be more--- impressed. :)-----------------------------------------------------------------------------instance Read OrdA where-  readsPrec _ i = [ (OrdA j, str) | (j, str) <- reads i ]--deriving instance Typeable OrdA-deriving instance Generic OrdA--heapProgramTest :: IO ()-heapProgramTest = SC.smartCheck SC.scStdArgs (\h -> (prop_ToSortedList h))--instance SC.SubTypes OrdA-instance (SC.SubTypes a, Ord a, Arbitrary a, Generic a)-         => SC.SubTypes (Heap a)-instance (SC.SubTypes a, Arbitrary a, Generic a)-         => SC.SubTypes (HeapP a)-instance (SC.SubTypes a, Ord a, Arbitrary a, Generic a)-         => SC.SubTypes (HeapPP a)--instance (Ord a, Arbitrary a) => Arbitrary (Heap a) where-  arbitrary = do p <- arbitrary :: Gen (HeapP a)-                 return $ heap p------------------------------------------------------------------------------- skew heaps--- Smallest values on top.--data Heap a-  = Node a (Heap a) (Heap a)-  | Nil- deriving ( Eq, Ord, Show, Read, Typeable, Generic )--empty :: Heap a-empty = Nil--isEmpty :: Heap a -> Bool-isEmpty Nil = True-isEmpty _   = False--unit :: a -> Heap a-unit x = Node x empty empty--size :: Heap a -> Int-size Nil            = 0-size (Node _ h1 h2) = 1 + size h1 + size h2--insert :: Ord a => a -> Heap a -> Heap a-insert x h = unit x `merge` h--removeMin :: Ord a => Heap a -> Maybe (a, Heap a)-removeMin Nil            = Nothing-removeMin (Node x h1 h2) = Just (x, h1 `merge` h2)--merge :: Ord a => Heap a -> Heap a -> Heap a-h1  `merge` Nil = h1-Nil `merge` h2  = h2-h1@(Node x h11 h12) `merge` h2@(Node y h21 h22)-  | x <= y    = Node x (h12 `merge` h2) h11-  | otherwise = Node y (h22 `merge` h1) h21--fromList :: Ord a => [a] -> Heap a-fromList xs = merging [ unit x | x <- xs ]- where-  merging []  = empty-  merging [h] = h-  merging hs  = merging (sweep hs)--  sweep []         = []-  sweep [h]        = [h]-  sweep (h1:h2:hs) = (h1 `merge` h2) : sweep hs--toList :: Heap a -> [a]-toList h = toList' [h]- where-  toList' []                  = []-  toList' (Nil          : hs) = toList' hs-  toList' (Node x h1 h2 : hs) = x : toList' (h1:h2:hs)--toSortedList :: Ord a => Heap a -> [a]-toSortedList Nil            = []-toSortedList (Node x h1 h2) = x : toList (h1 `merge` h2)------------------------------------------------------------------------------- heap programs--data HeapP a-  = Empty-  | Unit a-  | Insert a (HeapP a)-  | SafeRemoveMin (HeapP a)-  | Merge (HeapP a) (HeapP a)-  | FromList [a]- deriving ( Show, Read, Typeable, Generic )--heap :: Ord a => HeapP a -> Heap a-heap Empty             = empty-heap (Unit x)          = unit x-heap (Insert x p)      = insert x (heap p)-heap (SafeRemoveMin p) = case removeMin (heap p) of-                           Nothing    -> empty -- arbitrary choice-                           Just (_,h) -> h-heap (Merge p q)       = heap p `merge` heap q-heap (FromList xs)     = fromList xs--instance Arbitrary a => Arbitrary (HeapP a) where-  arbitrary = sized arbHeapP-   where-    arbHeapP s =-      frequency-      [ (1, do return Empty)-      , (1, do x <- arbitrary-               return (Unit x))-      , (s, do x <- arbitrary-               p <- arbHeapP s1-               return (Insert x p))-      , (s, do p <- arbHeapP s1-               return (SafeRemoveMin p))-      , (s, do p <- arbHeapP s2-               q <- arbHeapP s2-               return (Merge p q))-      , (1, do xs <- arbitrary-               return (FromList xs))-      ]-     where-      s1 = s-1-      s2 = s`div`2---  -- shrink (Unit x)          = [ Unit x' | x' <- shrink x ]-  -- shrink (FromList xs)     = [ Unit x | x <- xs ]-  --                         ++ [ FromList xs' | xs' <- shrink xs ]-  -- shrink (Insert x p)      = [ p ]-  --                         ++ [ Insert x p' | p' <- shrink p ]-  --                         ++ [ Insert x' p | x' <- shrink x ]-  -- shrink (SafeRemoveMin p) = [ p ]-  --                         ++ [ SafeRemoveMin p' | p' <- shrink p ]-  -- shrink (Merge p q)       = [ p, q ]-  --                         ++ [ Merge p' q | p' <- shrink p ]-  --                         ++ [ Merge p q' | q' <- shrink q ]-  -- shrink _                 = []--data HeapPP a = HeapPP (HeapP a) (Heap a)- deriving ( Show, Read, Typeable, Generic )--instance (Ord a, Arbitrary a) => Arbitrary (HeapPP a) where-  arbitrary =-    do p <- arbitrary-       return (HeapPP p (heap p))--  -- shrink (HeapPP p _) =-  --   [ HeapPP p' (heap p') | p' <- shrink p ]------------------------------------------------------------------------------- properties--(==?) :: Heap OrdA -> [OrdA] -> Bool-h ==? xs = sort (toList h) == sort xs--prop_Empty =-  empty ==? []--prop_IsEmpty (HeapPP _ h) =-  isEmpty h == null (toList h)--prop_Unit x =-  unit x ==? [x]--prop_Size (HeapPP _ h) =-  size h == length (toList h)--prop_Insert x (HeapPP _ h) =-  insert x h ==? (x : toList h)--prop_RemoveMin (HeapPP _ h) =-  cover (size h > 1) 80 "non-trivial" $-  case removeMin h of-    Nothing     -> h ==? []-    Just (x,h') -> x == minimum (toList h) && h' ==? (toList h \\ [x])--prop_Merge (HeapPP _ h1) (HeapPP _ h2) =-  (h1 `merge` h2) ==? (toList h1 ++ toList h2)--prop_FromList xs =-  fromList xs ==? xs--prop_ToSortedList :: HeapPP OrdA -> Bool-prop_ToSortedList (HeapPP _ h) =-  h ==? xs && xs == sort xs- where-  xs = toSortedList h------------------------------------------------------------------------------- main---- main = $(quickCheckAll)------------------------------------------------------------------------------- the end.
− examples/LambdaCalc.hs
@@ -1,141 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}---- Copied from <http://augustss.blogspot.com/2007/10/simpler-easier-in-recent-paper-simply.html>--module LambdaCalc where--import Data.List-import Data.Typeable--import Control.Monad-import GHC.Generics--import Test.QuickCheck--import Test.SmartCheck--type Sym = String--data Expr-        = Var Sym-        | App Expr Expr-        | Lam Sym Expr-        deriving (Eq, Read, Show, Typeable, Generic)--freeVars :: Expr -> [Sym]-freeVars (Var v) = [v]-freeVars (App f a) = freeVars f `union` freeVars a-freeVars (Lam i e) = freeVars e \\ [i]--subst :: Sym -> Expr -> Expr -> Expr-subst v x b = sub b-  where sub e@(Var i) = if i == v then x else e-        sub (App f a) = App (sub f) (sub a)-        sub (Lam i e) =-            if v == i then-                Lam i e-            else if i `elem` fvx then-                let i' = cloneSym e i-                    e' = substVar i i' e-                in  Lam i' (sub e')-            else-                Lam i (sub e)-        fvx = freeVars x-        cloneSym e i = loop i-           where loop i' = if i' `elem` vs then loop (i ++ "'") else i'-                 vs = fvx ++ freeVars e--substVar :: Sym -> Sym -> Expr -> Expr-substVar v v' e = subst v (Var v') e--alphaEq :: Expr -> Expr -> Bool-alphaEq (Var v)   (Var v')    = v == v'-alphaEq (App f a) (App f' a') = alphaEq f f' && alphaEq a a'-alphaEq (Lam v e) (Lam v' e') = alphaEq e (substVar v' v e')-alphaEq _ _ = False--nf :: Expr -> Expr-nf ee = spine ee []-  where spine (App f a) as = spine f (a:as)-        spine (Lam v e) [] = Lam v (nf e)-        spine (Lam v e) (a:as) = spine (subst v a e) as-        spine f as = app f as-        app f as = foldl App f (map nf as)--betaEq :: Expr -> Expr -> Bool-betaEq e1 e2 = alphaEq (nf e1) (nf e2)--z,s,m,n :: Expr-[z,s,m,n] = map (Var . (:[])) "zsmn"-app2 :: Expr -> Expr -> Expr -> Expr-app2 f x y = App (App f x) y-zero, one, two, three, plus :: Expr-zero  = Lam "s" $ Lam "z" z-one   = Lam "s" $ Lam "z" $ App s z-two   = Lam "s" $ Lam "z" $ App s $ App s z-three = Lam "s" $ Lam "z" $ App s $ App s $ App s z-plus  = Lam "m" $ Lam "n" $ Lam "s" $ Lam "z" $ app2 m s (app2 n s z)--test0 :: Bool-test0 = betaEq (app2 plus one two) three-------------------------------------------------------------------------------------instance SubTypes Expr-instance SubTypes Pr-------------------------------------------------------------------------------------data Pr = Pr Expr Expr-  deriving (Read, Show, Typeable, Generic)--instance Arbitrary Expr where-  arbitrary = sized mkE-    where-    mkE 0 = liftM Var vars-    mkE x = oneof [ liftM2 App (liftM2 Lam vars mkE') mkE'-                  , liftM2 Lam vars mkE'-                  ]-      where-      mkE' = mkE =<< choose (0, x-1)--vars :: Gen [Char]-vars = oneof $ map return ["x", "y", "z"]--instance Arbitrary Pr where-  arbitrary = do expr  <- arbitrary-                 return $ Pr expr expr--------------------------------------------------------------------------------------- prop0 :: Pr -> Property--- prop0 (Pr (e0, e1)) = alphaEq e0 e1 ==> betaEq e0 e1---- if you do a beta reduction to nf--- prop1 :: Pr -> ScProperty--- prop1 (Pr e0 e1) = -- Timeout due to possible non-termination---   within 1000 $ alphaEq e0 e1 --> betaEq e0 (substVar "x" "y" e1)---- lambdaTest :: IO ()--- lambdaTest = smartCheck args prop1---   where args = scStdArgs { qcArgs = stdArgs { maxSuccess = 100---                                             , maxSize    = 100---                                             }---                          }-------------------------------------------------------------------------------------- Cruft--{--nonDet = App x x-  where-  x = Lam "x" (App (Var "x") (Var "x"))---xx = (App (Lam "`" (App (Lam "\SI" (Var "f")) -                        (App (Lam "" (Var "O\172")) -                             (Var "3UC")))))--aa (Pr a b) = alphaEq a b--}
− examples/MutualRecData.hs
@@ -1,60 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}--module MutualRecData where--import Test.SmartCheck-import Test.QuickCheck hiding (Result)--import Data.Tree-import Control.Monad.State-import Data.Typeable--import GHC.Generics------------------------------------------------------------------------------------data M = M N N Int | P-  deriving (Typeable, Show, Eq, Read, Generic)--instance SubTypes M--data N = N M Int String-  deriving (Typeable, Show, Eq, Read, Generic)--instance SubTypes N------------------------------------------------------------------------------------instance Arbitrary M where-  arbitrary =-    sized $ \n -> if n == 0 then return P-                    else oneof [ return P-                               , liftM3 M (resize (n-1) arbitrary)-                                          (resize (n-1) arbitrary)-                                          arbitrary-                               ]--instance Arbitrary N where-  arbitrary = liftM3 N arbitrary arbitrary arbitrary------------------------------------------------------------------------------------prop0 :: M -> Bool-prop0 (M _ _ a) = a < 100-prop0 _         = True--mutRecTest :: IO ()-mutRecTest = smartCheck args prop0-  where-  args = scStdArgs { qcArgs = stdArgs {maxSuccess = 1000} }------------------------------------------------------------------------------------xx :: M-xx = M (N (M (N P 1 "goo") (N P 7 "foo") 8) 3 "hi") (N P 4 "bye") 6-yy :: Forest Int-yy = [Node 0 [Node 1 [], Node 2 []], Node 3 [Node 4 [], Node 5 [Node 6 []]]]----------------------------------------------------------------------------------
− examples/Tests.hs
@@ -1,15 +0,0 @@-module Main where--import Div0-import MutualRecData-import Heap_Program---import LambdaCalc---import Protocol--main :: IO ()-main = do-  divTest-  mutRecTest-  heapProgramTest---  lambdaTest---  protocolTest
+ qc-tests/Tests.hs view
@@ -0,0 +1,95 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE StandaloneDeriving #-}++{-# OPTIONS_GHC -fno-warn-orphans #-}++-- QuickCheck tests for the implementation of SmartCheck.++module Main where++import qualified Test.QuickCheck as Q++import Data.Maybe+import Data.Tree+import Control.Monad+import GHC.Generics+import Test.SmartCheck.DataToTree+import Test.SmartCheck.Types++--------------------------------------------------------------------------------++deriving instance Generic a =>  Generic (Tree a)+instance (SubTypes a, Generic a) => SubTypes (Tree a)++instance Q.Arbitrary a => Q.Arbitrary (Tree a) where+  arbitrary = Q.sized mkT+    where+    mkT 0 = Q.arbitrary >>= \a -> return (Node a [])+    mkT n = do len <- Q.choose (0, 4)+               a <- Q.arbitrary+               ls <- replicateM len mkT'+               return $ Node a ls+      where mkT' = mkT =<< Q.choose (0, n-1)++instance Q.Arbitrary Idx where+  arbitrary = liftM2 Idx Q.arbitrary Q.arbitrary++--------------------------------------------------------------------------------++-- Just to prevent us from getting too many Nothings from indexing too deeply.+dep :: Maybe Int+dep = Just 5++--------------------------------------------------------------------------------++-- If you take from v a sub-value v' at index i, then replace v' at index i, you+-- get v back.+prop_getReplaceIdem ::+  Tree Int -> Q.NonNegative Int -> Q.NonNegative Int -> Bool+prop_getReplaceIdem v (Q.NonNegative i) (Q.NonNegative j) =+  let x = getAtIdx v idx dep in+  case x of+    Nothing -> True+    Just st -> rep st+  where+  idx = Idx i j+  rep (SubT v') = replaceAtIdx v idx v' == Just v++--------------------------------------------------------------------------------++-- Morally, getAtIdx v idx Nothing == rootLabel $ getIdxForest (subTypes v) idx+--+-- That is, they return the same value, except getIdxForest returns the whole+-- tree.+prop_forestTreeEq :: Tree Int -> Q.Positive Int -> Q.NonNegative Int -> Bool+prop_forestTreeEq v (Q.Positive i) (Q.NonNegative j) =+  let mx = getAtIdx v idx Nothing :: Maybe SubT in+  let my = getIdxForest (subTypes v) idx :: Maybe (Tree SubT) in+  (isNothing mx && isNothing my) || go mx my == Just True+  where+  -- XXX Hack! Since SubTypes doesn't derive Eq.+  exEq (SubT x) (SubT y) = show x == show y+  idx = Idx i j+  go a b = do+   x <- a+   y <- b+   return $ exEq x (rootLabel y)++--------------------------------------------------------------------------------+-- Prop:+-- null (subTypes v) iff null (showForest v)+--------------------------------------------------------------------------------+++-- Some random values.+vals :: IO ()+vals = Q.sample (Q.resize 5 Q.arbitrary :: Q.Gen (Tree Int))++main :: IO ()+main = do+  Q.quickCheck prop_getReplaceIdem+  Q.quickCheck prop_forestTreeEq++--------------------------------------------------------------------------------
smartcheck.cabal view
@@ -1,5 +1,5 @@ Name:                smartcheck-Version:             0.1+Version:             0.2 Synopsis:            A smarter QuickCheck. Homepage:            https://github.com/leepike/SmartCheck Description:         See the README.md.@@ -18,6 +18,10 @@   type:     git   location: https://github.com/leepike/SmartCheck.git +flag regression-flag+  default:              False+  description:          add libraries for regression testing+ Library   Exposed-modules:   Test.SmartCheck,                      Test.SmartCheck.Args,@@ -28,15 +32,27 @@                      Test.SmartCheck.Reduce,                      Test.SmartCheck.Render,                      Test.SmartCheck.SmartGen,+                     Test.SmartCheck.Test,                      Test.SmartCheck.Types -  Build-depends:     base >= 4.0 && < 5,-                     QuickCheck >= 2.6,-                     mtl,-                     random >= 1.0.1.1,-                     containers >= 0.4,-                     generic-deriving >= 1.2.1,-                     ghc-prim+  if flag(regression-flag)+    Build-depends:     base >= 4.0 && < 5,+                       QuickCheck >= 2.7,+                       mtl,+                       random >= 1.0.1.1,+                       containers >= 0.4,+                       generic-deriving >= 1.2.1,+                       ghc-prim,+                       testing-feat,+                       lazysmallcheck+  else+    Build-depends:     base >= 4.0 && < 5,+                       QuickCheck >= 2.7,+                       mtl,+                       random >= 1.0.1.1,+                       containers >= 0.4,+                       generic-deriving >= 1.2.1,+                       ghc-prim    default-language:  Haskell2010 @@ -49,16 +65,33 @@     -caf-all     -fno-warn-orphans -executable sc-regression+-- Tries some SmartCheck on small examples in a REPL.+-- DEPRECATED in favor of the regression suite.+-- executable sc-examples+--   Hs-source-dirs:    examples+--   Main-is:           Tests.hs+--   Other-modules:     Div0,+--                      MutualRecData,+--                      Heap_Program,+--                      LambdaCalc+--   Build-depends:     base >= 4.0 && < 5,+--                      smartcheck >= 0.2,+--                      QuickCheck >= 2.7,+--                      mtl,+--                      random >= 1.0.1.1,+--                      containers >= 0.4,+--                      generic-deriving >= 1.2.1,+--                      ghc-prim+--   Default-language:  Haskell2010+--   Ghc-options:       -Wall++-- QuickCheck some basic properties about SmartCheck.+executable sc-qc+  Hs-source-dirs:    qc-tests   Main-is:           Tests.hs-  Other-modules:     Div0,-                     MutualRecData,-                     Heap_Program,-                     LambdaCalc-  Hs-source-dirs:    examples   Build-depends:     base >= 4.0 && < 5,-                     smartcheck,-                     QuickCheck >= 2.4.2,+                     smartcheck >= 0.2,+                     QuickCheck >= 2.7,                      mtl,                      random >= 1.0.1.1,                      containers >= 0.4,
src/Test/SmartCheck.hs view
@@ -5,16 +5,14 @@ -- | Interface module.  module Test.SmartCheck-  ( -- ** Main interface function.+  ( -- ** Main SmartCheck interface.     smartCheck -  -- ** Type of SmartCheck properties.-  , ScProperty()-  -- ** Implication for SmartCheck properties.-  , (-->)+    -- ** User-suppplied counterexample interface.+  , smartCheckInput    -- ** Run QuickCheck and get a result.-  , runQCInit+  , runQC    -- ** Arguments   , module Test.SmartCheck.Args@@ -31,75 +29,83 @@   ) where  import Test.SmartCheck.Args-import Test.SmartCheck.Types+import Test.SmartCheck.ConstructorGen+import Test.SmartCheck.Extrapolate import Test.SmartCheck.Matches import Test.SmartCheck.Reduce-import Test.SmartCheck.Extrapolate import Test.SmartCheck.Render-import Test.SmartCheck.ConstructorGen+import Test.SmartCheck.Test+import Test.SmartCheck.Types  import qualified Test.QuickCheck as Q  import Generics.Deriving+import Control.Monad (when)  --------------------------------------------------------------------------------  -- | Main interface function.-smartCheck :: forall a prop.-  ( Read a, Q.Arbitrary a, SubTypes a+smartCheck ::+  ( SubTypes a   , Generic a, ConNames (Rep a)-  , ScProp prop, Q.Testable prop+  , Q.Testable prop   ) => ScArgs -> (a -> prop) -> IO ()-smartCheck args scProp = do-  -- Run standard QuickCheck or read in value.-  (mcex, prop) <--    if qc args then runQCInit (qcArgs args) scProp-      else do smartPrtLn "Input value to SmartCheck:"-              mcex <- fmap Just (readLn :: IO a)-              return (mcex, propify scProp)+smartCheck args scProp =+  smartCheckRun args =<< runQC (qcArgs args) scProp +smartCheckInput :: forall a prop.+  ( SubTypes a+  , Generic a, ConNames (Rep a)+  , Q.Testable prop+  , Read a+  ) => ScArgs -> (a -> prop) -> IO ()+smartCheckInput args scProp = do+  smartPrtLn "Input value to SmartCheck:"+  mcex <- fmap Just (readLn :: IO a)+  smartCheckRun args (mcex, Q.property . scProp)++smartCheckRun :: forall a.+  ( SubTypes a+  , Generic a, ConNames (Rep a)+  ) => ScArgs -> (Maybe a, a -> Q.Property) -> IO ()+smartCheckRun args (origMcex, origProp) = do   smartPrtLn $-    "(If any stage takes too long, try modifying the standard "+    "(If any stage takes too long, try modifying SmartCheck's standard "       ++ "arguments (see Args.hs).)"-  runSmartCheck prop mcex-+  smartCheck' [] origMcex origProp   where-  runSmartCheck :: (a -> Q.Property) -> Maybe a -> IO ()-  runSmartCheck origProp = smartCheck' [] origProp+  smartCheck' :: [(a, Replace Idx)]+              -> Maybe a+              -> (a -> Q.Property)+              -> IO ()+  smartCheck' ds mcex prop =+    maybe (maybeDoneMsg >> return ()) go mcex     where-    smartCheck' :: [(a, Replace Idx)]-                -> (a -> Q.Property)-                -> Maybe a-                -> IO ()-    smartCheck' ds prop mcex = do-      maybe (maybeDoneMsg >> return ()) go mcex-      where-      go cex = do-          -- Run the smart reduction algorithm.-        d <- smartRun args cex prop-        -- If we asked to extrapolate values, do so.-        valIdxs <- forallExtrap args d origProp-        -- If we asked to extrapolate constructors, do so, again with the-        -- original property.-        csIdxs <- existsExtrap args d valIdxs origProp--        let replIdxs = Replace valIdxs csIdxs-        -- If either kind of extrapolation pass yielded fruit, prettyprint it.-        showExtrapOutput args valIdxs csIdxs replIdxs d+    go cex = do+        -- Run the smart reduction algorithm.+      d <- smartRun args cex prop+      -- If we asked to extrapolate values, do so.+      valIdxs <- forallExtrap args d origProp+      -- If we asked to extrapolate constructors, do so, again with the+      -- original property.+      csIdxs <- existsExtrap args d valIdxs origProp -        -- Ask the user if she wants to try again.-        runAgainMsg-        s <- getLine+      let replIdxs = Replace valIdxs csIdxs+      -- If either kind of extrapolation pass yielded fruit, prettyprint it.+      showExtrapOutput args valIdxs csIdxs replIdxs d+      -- Try again?+      runAgainMsg -        if s == ""-          -- If so, then loop, with the new prop.-          then do let oldVals  = (d,replIdxs):ds-                  let matchesProp a =-                              not (matchesShapes a oldVals)-                        Q.==> prop a-                  mcex' <- runQC (qcArgs args) matchesProp-                  smartCheck' oldVals matchesProp mcex'-          else smartPrtLn "Done."+      s <- getLine+      if s == ""+        -- If so, then loop, with the new prop.+        then do let oldVals  = (d,replIdxs):ds+                let matchesProp a =+                            not (matchesShapes a oldVals)+                      Q.==> prop a+                (mcex', _) <- runQC (qcArgs args) (Q.noShrinking . matchesProp)+                smartCheck' oldVals mcex' matchesProp+        else smartPrtLn "Done."    maybeDoneMsg = smartPrtLn "No value to smart-shrink; done." @@ -127,9 +133,10 @@ showExtrapOutput :: SubTypes a1                  => ScArgs -> [a] -> [a] -> Replace Idx -> a1 -> IO () showExtrapOutput args valIdxs csIdxs replIdxs d =-  if (runForall args || runExists args) && (not $ null (valIdxs ++ csIdxs))-    then output-    else smartPrtLn "Could not extrapolate a new value."+  when (runForall args || runExists args) $ do+    if null (valIdxs ++ csIdxs)+      then smartPrtLn "Could not extrapolate a new value."+      else output   where   output = do     putStrLn ""@@ -146,103 +153,26 @@  -------------------------------------------------------------------------------- --- XXX I have to parse a string from QC to get the counterexamples.---- | Run QuickCheck initially, to get counterexamples for each argument,--- includding the one we want to focus on for SmartCheck, plus a `Property`.-runQCInit :: (Show a, Read a, Q.Arbitrary a, ScProp prop, Q.Testable prop)+-- | Run QuickCheck, to get a counterexamples for each argument, including the+-- one we want to focus on for SmartCheck, which is the first argument.  That+-- argument is never shrunk by QuickCheck, but others may be shrunk by+-- QuickCheck.  Returns the value (if it exists) and a 'Property' (by applying+-- the 'property' method to the 'Testable' value).  In each iteration of+-- 'runQC', non-SmartCheck arguments are not necessarily held constant+runQC :: forall a prop . (Show a, Q.Arbitrary a, Q.Testable prop)           => Q.Args -> (a -> prop) -> IO (Maybe a, a -> Q.Property)-runQCInit args scProp = do-  res <- Q.quickCheckWithResult args (genProp $ propify scProp)-  return $ maybe-    -- 2nd arg should never be evaluated if the first arg is Nothing.-    (Nothing, errorMsg "Bug in runQCInit")-    ((\(cex, p) -> (Just cex, p)) . parse)-    (getOut res)-  where-  parse outs = (read $ head cexs, prop')-    where cexs = lenChk ((< 2) . length) outs-          prop' = propifyWithArgs (tail cexs) scProp---- | Run QuickCheck only analyzing the SmartCheck value, holding the other--- values constant.-runQC :: (Show a, Read a, Q.Arbitrary a)-      => Q.Args -> (a -> Q.Property) -> IO (Maybe a)-runQC args prop = do-  res <- Q.quickCheckWithResult args (genProp prop)-  return $ fmap parse (getOut res)-  where-  parse outs = read $ head cexs-    where cexs = lenChk ((/= 2) . length) outs--lenChk :: ([String] -> Bool) -> [String] -> [String]-lenChk chk ls = if chk ls then errorMsg "No value to SmartCheck!"-                   else tail ls+runQC args scProp = do+  (mCex, res) <- scQuickCheckWithResult args scProp+  return $ if failureRes res+             then (mCex,    Q.property . scProp)+             else (Nothing, Q.property . scProp) -getOut :: Q.Result -> Maybe [String]-getOut res =+-- | Returns 'True' if a counterexample is returned and 'False' otherwise.+failureRes :: Q.Result -> Bool+failureRes res =   case res of-    Q.Failure _ _ _ _ _ _ _ out -> Just $ lines out-    _                           -> Nothing--genProp :: (Show a, Q.Testable prop, Q.Arbitrary a)-        => (a -> prop) -> Q.Property-genProp prop = Q.forAllShrink Q.arbitrary Q.shrink prop+    Q.Failure _ _ _ _ _ _ _ _ _ _ -> True+    _                             -> False  -------------------------------------------------------------------------------- --- | Type for SmartCheck properties.  Moral equivalent of QuickCheck's--- `Property` type.-data ScProperty = Implies (Bool, Bool)-                | Simple  Bool-  deriving (Show, Read, Eq)--instance Q.Testable ScProperty where-  property (Simple prop)         = Q.property prop-  property (Implies prop)        = Q.property (toQCImp prop)-  exhaustive (Simple prop)       = Q.exhaustive prop-  exhaustive (Implies prop)      = Q.exhaustive (toQCImp prop)---- same as ==>-infixr 0 -->--- | Moral equivalent of QuickCheck's `==>` operator.-(-->) :: Bool -> Bool -> ScProperty-pre --> post = Implies (pre, post)---- Helper function.-toQCImp :: (Bool, Bool) -> Q.Property-toQCImp (pre, post) = pre Q.==> post---- | Turn a function that returns a `Bool` into a QuickCheck `Property`.-class ScProp prop where-  scProperty :: [String] -> prop -> Q.Property-  qcProperty :: prop -> Q.Property---- | Instance without preconditions.-instance ScProp Bool where-  scProperty _ res = Q.property res-  qcProperty       = Q.property---- | Wrapped properties.-instance ScProp ScProperty where-  scProperty _ (Simple res)     = Q.property res-  scProperty _ (Implies prop)   = Q.property $ toQCImp prop--  qcProperty   (Simple res)     = Q.property res-  qcProperty   (Implies prop)   = Q.property $ toQCImp prop---- | Beta-reduction.-instance (Q.Arbitrary a, Q.Testable prop, Show a, Read a, ScProp prop)-  => ScProp (a -> prop) where-  scProperty (str:strs) f = Q.property $ scProperty strs (f (read str))-  scProperty _          _ = errorMsg "Insufficient values applied to property!"-  qcProperty              = Q.property--propifyWithArgs :: (Read a, ScProp prop)-  => [String] -> (a -> prop) -> (a -> Q.Property)-propifyWithArgs strs prop = \a -> scProperty strs (prop a)--propify :: ScProp prop => (a -> prop) -> (a -> Q.Property)-propify prop = \a -> qcProperty (prop a)----------------------------------------------------------------------------------
src/Test/SmartCheck/Args.hs view
@@ -18,10 +18,6 @@                                      --------------          , qcArgs       :: Q.Args    -- ^ QuickCheck arguments                                      ---------------         , qc           :: Bool      -- ^ Should we run QuickCheck?  (If not,-                                     --   you are expected to pass in data to-                                     --   analyze.)-                                     --------------          , scMaxSize    :: Int       -- ^ Maximum size of data to generate, in                                      --   terms of the size parameter of                                      --   QuickCheck's Arbitrary instance for@@ -64,7 +60,6 @@ scStdArgs :: ScArgs scStdArgs = ScArgs { format       = PrintTree                    , qcArgs       = Q.stdArgs-                   , qc           = True                    , scMaxSize    = 10                    , scMaxDepth   = Nothing                    ---------------------
src/Test/SmartCheck/Reduce.hs view
@@ -106,11 +106,11 @@ -- | Get the maximum depth of d's subforest at idx.  Intuitively, it's the -- maximum number of constructors you have *below* the constructor at idx.  So -- for a unary constructor C, the value [C, C, C]-+-- -- (:) C --   (:) C --     (:) C []-+-- -- At (Idx 0 0) in v, we're at C, so subValSize v (Idx 0 0) == 0. -- At (Idx 0 1) in v, we're at (C : C : []), so subValSize v (Idx 0 1) == 2, since -- we have the constructors :, C (or :, []) in the longest path underneath.
src/Test/SmartCheck/Render.hs view
@@ -83,7 +83,7 @@   f :: Tree VarRepl -> (String, Idx) -> Tree VarRepl   f tree (var, idx) = Node (rootLabel tree) $     case getIdxForest sf idx of-      Nothing                 -> errorMsg "replaceWithVars"+      Nothing                 -> errorMsg "replaceWithVars1"       Just (Node (Right _) _) -> sf -- Don't replace anything       Just (Node (Left  _) _) -> forestReplaceChildren sf idx (Right var) @@ -94,7 +94,7 @@   -- data.  showForest is one of our generic methods.   t :: Tree VarRepl   t = let forest = showForest d in-      if null forest then errorMsg "replaceWithVars"+      if null forest then errorMsg "replaceWithVars2"          else fmap Left (head forest) -- Should be a singleton    -- Note: we put value idxs before constrs, since they take precedence.
src/Test/SmartCheck/SmartGen.hs view
@@ -11,8 +11,9 @@ import Test.SmartCheck.Types import Test.SmartCheck.DataToTree -import qualified Test.QuickCheck.Gen as Q-import qualified Test.QuickCheck as Q hiding (Result)+import qualified Test.QuickCheck.Gen      as Q+import qualified Test.QuickCheck.Random   as Q+import qualified Test.QuickCheck          as Q hiding (Result) import qualified Test.QuickCheck.Property as P  import Prelude hiding (max)@@ -24,7 +25,7 @@ -- | Driver for iterateArb. iterateArbIdx :: SubTypes a               => a -> (Idx, Maybe Int) -> Int -> Int-              -> (a -> Q.Property) -> IO (Int, Result a)+              -> (a -> P.Property) -> IO (Int, Result a) iterateArbIdx d (idx, max) tries sz prop =   maybe (errorMsg "iterateArb 0")         (\ext -> iterateArb d ext idx tries sz prop)@@ -45,18 +46,18 @@   -> Idx                -- ^ Index of sub-value.   -> Int                -- ^ Maximum number of iterations.   -> Int                -- ^ Maximum size of value to generate.-  -> (a -> Q.Property)  -- ^ Property.+  -> (a -> P.Property)      -- ^ Property.   -> IO (Int, Result a) -- ^ Number of times precondition is passed and returned                         -- result. iterateArb d ext idx tries max prop = do-  g <- newStdGen+  g <- Q.newQCGen   iterateArb' (0, FailedPreCond) g 0 0   where   newMax SubT { unSubT = v } = valDepth v    -- Main loop.  We break out if we ever satisfy the property.  Otherwise, we   -- return the latest value.-  iterateArb' :: (Int, Result a) -> StdGen -> Int -> Int -> IO (Int, Result a)+  iterateArb' :: (Int, Result a) -> Q.QCGen -> Int -> Int -> IO (Int, Result a)   iterateArb' (i, res) g try currMax     -- We've exhausted the number of iterations.     | try >= tries = return (i, res)@@ -74,8 +75,8 @@               Result x      -> return (i+1, Result x)     where     (size, g0) = randomR (0, currMax) g-    s = sample ext g size     sample SubT { unSubT = v } = newVal v+    s = sample ext g size     rec res' =       iterateArb' res' g0 (try + 1)         -- XXX what ratio is right to increase size of values?  This gives us@@ -90,11 +91,10 @@ -- | Make a new random value given a generator and a max size.  Based on the -- value's type's arbitrary instance. newVal :: forall a. (SubTypes a, Q.Arbitrary a)-       => a -> StdGen -> Int -> SubT+       => a -> Q.QCGen -> Int -> SubT newVal _ g size =   let Q.MkGen m = Q.resize size (Q.arbitrary :: Q.Gen a) in-  let v = m g size in-  subT v+  subT (m g size)  -------------------------------------------------------------------------------- @@ -107,7 +107,7 @@  -- | Make a QuickCheck Result by applying a property function to a value and -- then get out the Result using our result type.-resultify :: (a -> Q.Property) -> a -> IO (Result a)+resultify :: (a -> P.Property) -> a -> IO (Result a) resultify prop a = do   P.MkRose r _ <- res fs   return $ maybe FailedPreCond -- Failed precondition (discard)@@ -120,8 +120,9 @@     | not b && not (P.expect r) = Result a -- expected failure and got it     | otherwise                 = FailedProp -- We'll just discard it. -  Q.MkGen { Q.unGen = f } = prop a :: Q.Gen P.Prop-  fs  = P.unProp $ f err err       :: P.Rose P.Result+  P.MkProperty { P.unProperty = Q.MkGen { Q.unGen = f } }+      = prop a :: P.Property+  fs  = P.unProp $ f err err            :: P.Rose P.Result   res = P.protectRose . P.reduceRose    -- XXX A hack!  Means we failed the property because it failed, not because of
+ src/Test/SmartCheck/Test.hs view
@@ -0,0 +1,392 @@+{-++The following is modified by Lee Pike (2014) and still retains the following+license:++Copyright (c) 2000-2012, Koen Claessen+Copyright (c) 2006-2008, Björn Bringert+Copyright (c) 2009-2012, Nick Smallbone+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++- Redistributions of source code must retain the above copyright notice,+  this list of conditions and the following disclaimer.+- Redistributions in binary form must reproduce the above copyright+  notice, this list of conditions and the following disclaimer in the+  documentation and/or other materials provided with the distribution.+- Neither the names of the copyright owners nor the names of the+  contributors may be used to endorse or promote products derived+  from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+-}++{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE ExistentialQuantification #-}++-- | SmartCheck's interface to QuickCheck.++module Test.SmartCheck.Test+  ( scQuickCheckWithResult+  , stdArgs+  ) where++--------------------------------------------------------------------------+-- imports++import Prelude hiding (break)++import Test.QuickCheck+import Test.QuickCheck.Gen+import Test.QuickCheck.Property hiding ( Result( reason, theException) )+import qualified Test.QuickCheck.Property as P+import Test.QuickCheck.Text+import Test.QuickCheck.State+import Test.QuickCheck.Exception+import Test.QuickCheck.Random+import System.Random(split)++import Data.Char+  ( isSpace+  )++import Data.List+  ( sort+  , group+  , groupBy+  , intersperse+  )++--------------------------------------------------------------------------+-- quickCheck++-- | Our SmartCheck reimplementation of the main QuickCheck driver.  We want to+-- distinguish the first argument to a 'Testable' property to be SmartChecked.+-- In particular: the first argument will not be shrunk (even if there are+-- default shrink instances for the type).  However, the argument will be grown+-- according to the the 'maxSize' argument to QuickCheck, in accordance with its+-- generator.  Other arguments will be shrunk, if they have shrinking instances.+scQuickCheckWithResult :: forall a prop. (Show a, Arbitrary a, Testable prop)+  => Args -> (a -> prop) -> IO (Maybe a, Result)+scQuickCheckWithResult a p = (if chatty a then withStdioTerminal else withNullTerminal) $ \tm -> do+     rnd <- case replay a of+              Nothing      -> newQCGen+              Just (rnd,_) -> return rnd+     test MkState{ terminal                  = tm+                 , maxSuccessTests           = maxSuccess a+                 , maxDiscardedTests         = maxDiscardRatio a * maxSuccess a+                 , computeSize               = case replay a of+                                                 Nothing    -> computeSize'+                                                 Just (_,s) -> computeSize' `at0` s+                 , numSuccessTests           = 0+                 , numDiscardedTests         = 0+                 , numRecentlyDiscardedTests = 0+                 , collected                 = []+                 , expectedFailure           = False+                 , randomSeed                = rnd+                 , numSuccessShrinks         = 0+                 , numTryShrinks             = 0+                 , numTotTryShrinks          = 0+                 } flipProp+  where computeSize' n d+          -- e.g. with maxSuccess = 250, maxSize = 100, goes like this:+          -- 0, 1, 2, ..., 99, 0, 1, 2, ..., 99, 0, 2, 4, ..., 98.+          | n `roundTo` maxSize a + maxSize a <= maxSuccess a ||+            n >= maxSuccess a ||+            maxSuccess a `mod` maxSize a == 0 = (n `mod` maxSize a + d `div` 10) `min` maxSize a+          | otherwise =+            ((n `mod` maxSize a) * maxSize a `div` (maxSuccess a `mod` maxSize a) + d `div` 10) `min` maxSize a+        n `roundTo` m = (n `div` m) * m+        at0 _f s 0 0  = s+        at0 f _s n d  = f n d++        flipProp :: QCGen -> Int -> (a -> Prop)+        flipProp q i = \a' ->+          let p' = p a' in+          let g = unGen (unProperty (property p')) in+          g q i++--------------------------------------------------------------------------+-- main test loop++test :: Arbitrary a => State -> (QCGen -> Int -> (a -> Prop)) -> IO (Maybe a, Result)+test st f+  | numSuccessTests st   >= maxSuccessTests st   = doneTesting st f+  | numDiscardedTests st >= maxDiscardedTests st = giveUp st f+  | otherwise                                    = runATest st f++doneTesting :: State -> (QCGen -> Int -> (a -> Prop)) -> IO (Maybe a, Result)+doneTesting st _f =+  do -- CALLBACK done_testing?+     if expectedFailure st then+       putPart (terminal st)+         ( "+++ OK, passed "+        ++ show (numSuccessTests st)+        ++ " tests"+         )+      else+       putPart (terminal st)+         ( bold ("*** Failed!")+        ++ " Passed "+        ++ show (numSuccessTests st)+        ++ " tests (expected failure)"+         )+     success st+     theOutput <- terminalOutput (terminal st)+     return $ (Nothing, if expectedFailure st then+                          Success{ labels = summary st,+                                   numTests = numSuccessTests st,+                                   output = theOutput }+                        else NoExpectedFailure{ labels = summary st,+                                                numTests = numSuccessTests st,+                                                output = theOutput })++giveUp :: State -> (QCGen -> Int -> (a -> Prop)) -> IO (Maybe a, Result)+giveUp st _f =+  do -- CALLBACK gave_up?+     putPart (terminal st)+       ( bold ("*** Gave up!")+      ++ " Passed only "+      ++ show (numSuccessTests st)+      ++ " tests"+       )+     success st+     theOutput <- terminalOutput (terminal st)+     return ( Nothing+            , GaveUp{ numTests = numSuccessTests st+                    , labels   = summary st+                    , output   = theOutput+                    }+            )++runATest :: forall a. (Arbitrary a)+         => State+         -> (QCGen -> Int -> (a -> Prop))+         -> IO (Maybe a, Result)+runATest st f =+  do -- CALLBACK before_test+     putTemp (terminal st)+        ( "("+       ++ number (numSuccessTests st) "test"+       ++ concat [ "; " ++ show (numDiscardedTests st) ++ " discarded"+                 | numDiscardedTests st > 0+                 ]+       ++ ")"+        )+     let size = computeSize st (numSuccessTests st) (numRecentlyDiscardedTests st)++     let p :: a -> Prop+         p = f rnd1 size++     let genA :: QCGen -> Int -> a+         genA = unGen arbitrary+     let rndA = genA rnd1 size++     let mkRes res = return (Just rndA, res)++     MkRose res ts <- protectRose (reduceRose (unProp (p rndA)))+     callbackPostTest st res++     let continue break st' | abort res = break st'+                            | otherwise = test st'++     case res of+       MkResult{ok = Just True, stamp, expect} -> -- successful test+         do continue doneTesting+              st{ numSuccessTests           = numSuccessTests st + 1+                , numRecentlyDiscardedTests = 0+                , randomSeed                = rnd2+                , collected                 = stamp : collected st+                , expectedFailure           = expect+                } f++       MkResult{ok = Nothing, expect = expect} -> -- discarded test+         do continue giveUp+              st{ numDiscardedTests         = numDiscardedTests st + 1+                , numRecentlyDiscardedTests = numRecentlyDiscardedTests st + 1+                , randomSeed                = rnd2+                , expectedFailure           = expect+                } f++       MkResult{ok = Just False} -> -- failed test+         do if expect res+              then putPart (terminal st) (bold "*** Failed! ")+              else putPart (terminal st) "+++ OK, failed as expected. "+            (numShrinks, totFailed, lastFailed) <- foundFailure st res ts+            theOutput <- terminalOutput (terminal st)+            if not (expect res) then+              mkRes  Success{ labels = summary st,+                              numTests = numSuccessTests st+1,+                              output = theOutput }+             else+              mkRes  Failure{ -- correct! (this will be split first)+                              usedSeed       = randomSeed st+                            , usedSize       = size+                            , numTests       = numSuccessTests st+1+                            , numShrinks     = numShrinks+                            , numShrinkTries = totFailed+                            , numShrinkFinal = lastFailed+                            , output         = theOutput+                            , reason         = P.reason res+                            , theException   = P.theException res+                            , labels         = summary st+                            }+ where+  (rnd1,rnd2) = split (randomSeed st)++summary :: State -> [(String,Int)]+summary st = reverse+           . sort+           . map (\ss -> (head ss, (length ss * 100) `div` numSuccessTests st))+           . group+           . sort+           $ [ concat (intersperse ", " s')+             | s <- collected st+             , let s' = [ t | (t,_) <- s ]+             , not (null s')+             ]++success :: State -> IO ()+success st =+  case allLabels ++ covers of+    []    -> do putLine (terminal st) "."+    [pt]  -> do putLine (terminal st)+                  ( " ("+                 ++ dropWhile isSpace pt+                 ++ ")."+                  )+    cases -> do putLine (terminal st) ":"+                sequence_ [ putLine (terminal st) pt | pt <- cases ]+ where+  allLabels = reverse+            . sort+            . map (\ss -> (showP ((length ss * 100) `div` numSuccessTests st) ++ head ss))+            . group+            . sort+            $ [ concat (intersperse ", " s')+              | s <- collected st+              , let s' = [ t | (t,0) <- s ]+              , not (null s')+              ]++  covers = [ ("only " ++ show occurP ++ "% " ++ fst (head lps) ++ "; not " ++ show reqP ++ "%")+           | lps <- groupBy first+                  . sort+                  $ [ lp+                    | lps <- collected st+                    , lp <- maxi lps+                    , snd lp > 0+                    ]+           , let occurP = (100 * length lps) `div` maxSuccessTests st+                 reqP   = maximum (map snd lps)+           , occurP < reqP+           ]++  (x,_) `first` (y,_) = x == y++  maxi = map (\lps -> (fst (head lps), maximum (map snd lps)))+       . groupBy first+       . sort++  showP p = (if p < 10 then " " else "") ++ show p ++ "% "++--------------------------------------------------------------------------+-- main shrinking loop++foundFailure :: State -> P.Result -> [Rose P.Result] -> IO (Int, Int, Int)+foundFailure st res ts =+  do localMin st{ numTryShrinks = 0 } res res ts++localMin :: State -> P.Result -> P.Result -> [Rose P.Result] -> IO (Int, Int, Int)+localMin st MkResult{P.theException = Just e} lastRes _+  | isInterrupt e = localMinFound st lastRes+localMin st res _ ts = do+  putTemp (terminal st)+    ( short 26 (oneLine (P.reason res))+   ++ " (after " ++ number (numSuccessTests st+1) "test"+   ++ concat [ " and "+            ++ show (numSuccessShrinks st)+            ++ concat [ "." ++ show (numTryShrinks st) | numTryShrinks st > 0 ]+            ++ " shrink"+            ++ (if numSuccessShrinks st == 1+                && numTryShrinks st == 0+                then "" else "s")+             | numSuccessShrinks st > 0 || numTryShrinks st > 0+             ]+   ++ ")..."+    )+  r <- tryEvaluate ts+  case r of+    Left err ->+      localMinFound st+         (exception "Exception while generating shrink-list" err) { callbacks = callbacks res }+    Right ts' -> localMin' st res ts'++localMin' :: State -> P.Result -> [Rose P.Result] -> IO (Int, Int, Int)+localMin' st res [] = localMinFound st res+localMin' st res (t:ts) =+  do -- CALLBACK before_test+    MkRose res' ts' <- protectRose (reduceRose t)+    callbackPostTest st res'+    if ok res' == Just False+      then localMin st{ numSuccessShrinks = numSuccessShrinks st + 1,+                        numTryShrinks     = 0 } res' res ts'+      else localMin st{ numTryShrinks    = numTryShrinks st + 1,+                        numTotTryShrinks = numTotTryShrinks st + 1 } res res ts++localMinFound :: State -> P.Result -> IO (Int, Int, Int)+localMinFound st res =+  do let report = concat [+           "(after " ++ number (numSuccessTests st+1) "test",+           concat [ " and " ++ number (numSuccessShrinks st) "shrink"+                  | numSuccessShrinks st > 0+                  ],+           "): "+           ]+     if isOneLine (P.reason res)+       then putLine (terminal st) (P.reason res ++ " " ++ report)+       else do+         putLine (terminal st) report+         sequence_+           [ putLine (terminal st) msg+           | msg <- lines (P.reason res)+           ]+     callbackPostFinalFailure st res+     return (numSuccessShrinks st, numTotTryShrinks st - numTryShrinks st, numTryShrinks st)++--------------------------------------------------------------------------+-- callbacks++callbackPostTest :: State -> P.Result -> IO ()+callbackPostTest st res =+  sequence_ [ safely st (f st res) | PostTest _ f <- callbacks res ]++callbackPostFinalFailure :: State -> P.Result -> IO ()+callbackPostFinalFailure st res =+  sequence_ [ safely st (f st res) | PostFinalFailure _ f <- callbacks res ]++safely :: State -> IO () -> IO ()+safely st x = do+  r <- tryEvaluateIO x+  case r of+    Left e ->+      putLine (terminal st)+        ("*** Exception in callback: " ++ show e)+    Right x' ->+      return x'++--------------------------------------------------------------------------+-- the end.
src/Test/SmartCheck/Types.hs view
@@ -26,6 +26,8 @@ import GHC.Generics import Data.Tree import Data.Typeable+import Control.Applicative+import Control.Monad (ap)  -- For instances import Data.Word@@ -60,6 +62,10 @@   fmap _ FailedProp    = FailedProp   fmap f (Result a)    = Result (f a) +instance Applicative Result where+ pure  = return+ (<*>) = ap+ instance Monad Result where   return a            = Result a   FailedPreCond >>= _ = FailedPreCond@@ -109,10 +115,7 @@   show (SubT t) = show t  -- | This class covers algebraic datatypes that can be transformed into Trees.--- subTypes is the main method, placing values into trees.  For types that can't--- be put into a *structural* order (e.g., Int), we don't want SmartCheck to--- touch them, so that aren't placed in the tree (the baseType method tells--- subTypes which types have this property).+-- subTypes is the main method, placing values into trees. -- -- for a datatype with constructors A and C, --@@ -243,7 +246,9 @@   gsz (M1 a) = gsz a  instance (Show a, Q.Arbitrary a, SubTypes a, Typeable a) => GST (K1 i a) where-  gst (K1 a) = if baseType a then [] else [ Node (subT a) (subTypes a) ]+  gst (K1 a) = if baseType a+                 then []+                 else [ Node (subT a) (subTypes a) ]    grc (K1 a) forest c =     case forest of@@ -340,23 +345,26 @@ -- --  toConstrAndBase = toConstrAndBase' --  showForest    = showForest' --- For container types like list, if it's over a baseType, we don't want to--- evaluate the container either.  The intuition is that, e.g., for [Int], it'll--- be shrunk enough by QuickCheck and doesn't really have "interesting--- structure".+-- For example, this makes String a baseType automatically.+-- instance (Q.Arbitrary a, SubTypes a, Typeable a) => SubTypes [a] where+--   subTypes      = if baseType (undefined :: a) then \_ -> []+--                     else gst . from+--   baseType _    = baseType (undefined :: a)+--   replaceChild x forest y = if baseType (undefined :: a)+--                               then replaceChild' x forest y+--                               else fmap to $ grc (from x) forest y+--   toConstr      = if baseType (undefined :: a) then toConstr'+--                     else gtc . from+--   showForest    = if baseType (undefined :: a) then showForest'+--                     else gsf . from  -- For example, this makes String a baseType automatically. instance (Q.Arbitrary a, SubTypes a, Typeable a) => SubTypes [a] where-  subTypes      = if baseType (undefined :: a) then \_ -> []-                    else gst . from-  baseType _    = baseType (undefined :: a)-  replaceChild x forest y = if baseType (undefined :: a)-                              then replaceChild' x forest y-                              else fmap to $ grc (from x) forest y-  toConstr      = if baseType (undefined :: a) then toConstr'-                    else gtc . from-  showForest    = if baseType (undefined :: a) then showForest'-                    else gsf . from+  subTypes      = gst . from+  baseType _    = False+  replaceChild x forest y = fmap to $ grc (from x) forest y+  toConstr      = gtc . from+  showForest    = gsf . from  instance (Integral a, Q.Arbitrary a, SubTypes a, Typeable a)   => SubTypes (Ratio a) where