{-# OPTIONS_HADDOCK hide #-} module Test.QuickCheck.Features where import Test.QuickCheck.Property hiding (Result, reason) import qualified Test.QuickCheck.Property as P import Test.QuickCheck.Test import Test.QuickCheck.Gen import Test.QuickCheck.State import Test.QuickCheck.Text import qualified Data.Set as Set import Data.Set(Set) import Data.List import Data.IORef import Data.Maybe features :: [String] -> Set String -> Set String features labels classes = Set.fromList labels `Set.union` classes prop_noNewFeatures :: Testable prop => Set String -> prop -> Property prop_noNewFeatures feats prop = mapResult f prop where f res = case ok res of Just True | not (features (P.labels res) (Set.fromList (P.classes res)) `Set.isSubsetOf` feats) -> res{ok = Just False, P.reason = "New feature found"} _ -> res -- | Given a property, which must use 'label', 'collect', 'classify' or 'cover' -- to associate labels with test cases, find an example test case for each possible label. -- The example test cases are minimised using shrinking. -- -- For example, suppose we test @'Data.List.delete' x xs@ and record the number -- of times that @x@ occurs in @xs@: -- -- > prop_delete :: Int -> [Int] -> Property -- > prop_delete x xs = -- > classify (count x xs == 0) "count x xs == 0" $ -- > classify (count x xs == 1) "count x xs == 1" $ -- > classify (count x xs >= 2) "count x xs >= 2" $ -- > counterexample (show (delete x xs)) $ -- > count x (delete x xs) == max 0 (count x xs-1) -- > where count x xs = length (filter (== x) xs) -- -- 'labelledExamples' generates three example test cases, one for each label: -- -- >>> labelledExamples prop_delete -- *** Found example of count x xs == 0 -- 0 -- [] -- [] -- -- *** Found example of count x xs == 1 -- 0 -- [0] -- [] -- -- *** Found example of count x xs >= 2 -- 5 -- [5,5] -- [5] -- -- +++ OK, passed 100 tests: -- 78% count x xs == 0 -- 21% count x xs == 1 -- 1% count x xs >= 2 labelledExamples :: Testable prop => prop -> IO () labelledExamples prop = labelledExamplesWith stdArgs prop -- | A variant of 'labelledExamples' that takes test arguments. labelledExamplesWith :: Testable prop => Args -> prop -> IO () labelledExamplesWith args prop = labelledExamplesWithResult args prop >> return () -- | A variant of 'labelledExamples' that returns a result. labelledExamplesResult :: Testable prop => prop -> IO Result labelledExamplesResult prop = labelledExamplesWithResult stdArgs prop -- | A variant of 'labelledExamples' that takes test arguments and returns a result. labelledExamplesWithResult :: Testable prop => Args -> prop -> IO Result labelledExamplesWithResult args prop = withState args $ \state -> do let loop :: Set String -> State -> IO Result loop feats state = withNullTerminal $ \nullterm -> do res <- test state{terminal = nullterm} (property (prop_noNewFeatures feats prop)) let feats' = features (failingLabels res) (failingClasses res) case res of Failure{reason = "New feature found"} -> do putLine (terminal state) $ "*** Found example of " ++ concat (intersperse ", " (Set.toList (feats' Set.\\ feats))) mapM_ (putLine (terminal state)) (failingTestCase res) putStrLn "" loop (Set.union feats feats') state{randomSeed = usedSeed res, computeSize = computeSize state `at0` usedSize res} _ -> do out <- terminalOutput nullterm putStr out return res at0 f s 0 0 = s at0 f s n d = f n d loop Set.empty state