QuickCheck 2.7.3 → 2.7.4
raw patch · 7 files changed
+151/−23 lines, 7 filesdep +transformersdep ~QuickCheck
Dependencies added: transformers
Dependency ranges changed: QuickCheck
Files
- QuickCheck.cabal +8/−4
- Test/QuickCheck/All.hs +52/−4
- Test/QuickCheck/Arbitrary.hs +4/−10
- Test/QuickCheck/Modifiers.hs +61/−3
- Test/QuickCheck/Monadic.hs +12/−1
- Test/QuickCheck/Property.hs +11/−1
- examples/Heap.hs +3/−0
QuickCheck.cabal view
@@ -1,5 +1,5 @@ Name: QuickCheck-Version: 2.7.3+Version: 2.7.4 Cabal-Version: >= 1.8 Build-type: Simple License: BSD3@@ -10,7 +10,7 @@ Maintainer: QuickCheck developers <quickcheck@projects.haskell.org> Bug-reports: mailto:quickcheck@projects.haskell.org Tested-with: GHC >=6.8, Hugs, UHC-Homepage: http://code.haskell.org/QuickCheck+Homepage: https://github.com/nick8325/quickcheck Category: Testing Synopsis: Automatic testing of Haskell programs Description:@@ -34,7 +34,7 @@ source-repository this type: git location: https://github.com/nick8325/quickcheck- tag: 2.7.3+ tag: 2.7.4 flag base3 Description: Choose the new smaller, split-up base package.@@ -74,6 +74,10 @@ -- GHC-specific modules. if impl(ghc) Exposed-Modules: Test.QuickCheck.Function+ if impl(ghc >= 7)+ Build-depends: transformers >= 0.2+ else+ cpp-options: -DNO_TRANSFORMERS if impl(ghc >= 6.12) && flag(templateHaskell) Build-depends: template-haskell >= 2.4 Exposed-Modules: Test.QuickCheck.All@@ -137,6 +141,6 @@ main-is: Heap.hs build-depends: base,- QuickCheck == 2.7.3,+ QuickCheck == 2.7.4, template-haskell >= 2.4, test-framework >= 0.4 && < 0.9
Test/QuickCheck/All.hs view
@@ -22,6 +22,8 @@ import Data.List import Control.Monad +import qualified System.IO as S+ -- | Test a polymorphic property, defaulting all type variables to 'Integer'. -- -- Invoke as @$('polyQuickCheck' 'prop)@, where @prop@ is a property.@@ -32,11 +34,19 @@ -- @'quickCheck' $('monomorphic' \'prop)@. -- If you want to supply custom arguments to 'polyQuickCheck', -- you will have to combine 'quickCheckWith' and 'monomorphic' yourself.+--+-- If you want to use 'polyQuickCheck' in the same file where you defined the+-- property, the same scoping problems pop up as in 'quickCheckAll':+-- see the note there about @return []@. polyQuickCheck :: Name -> ExpQ polyQuickCheck x = [| quickCheck $(monomorphic x) |] -- | Test a polymorphic property, defaulting all type variables to 'Integer'. -- This is just a convenience function that combines 'verboseCheck' and 'monomorphic'.+--+-- If you want to use 'polyVerboseCheck' in the same file where you defined the+-- property, the same scoping problems pop up as in 'quickCheckAll':+-- see the note there about @return []@. polyVerboseCheck :: Name -> ExpQ polyVerboseCheck x = [| verboseCheck $(monomorphic x) |] @@ -46,6 +56,10 @@ -- -- For example, if @f@ has type @'Ord' a => [a] -> [a]@ -- then @$('monomorphic' 'f)@ has type @['Integer'] -> ['Integer']@.+--+-- If you want to use 'monomorphic' in the same file where you defined the+-- property, the same scoping problems pop up as in 'quickCheckAll':+-- see the note there about @return []@. monomorphic :: Name -> ExpQ monomorphic t = do ty0 <- fmap infoType (reify t)@@ -84,37 +98,71 @@ -- @$'forAllProperties'@ has type @('Property' -> 'IO' 'Result') -> 'IO' 'Bool'@. -- An example invocation is @$'forAllProperties' 'quickCheckResult'@, -- which does the same thing as @$'quickCheckAll'@.+--+-- 'forAllProperties' has the same issue with scoping as 'quickCheckAll':+-- see the note there about @return []@. forAllProperties :: Q Exp -- :: (Property -> IO Result) -> IO Bool forAllProperties = do Loc { loc_filename = filename } <- location when (filename == "<interactive>") $ error "don't run this interactively"- ls <- runIO (fmap lines (readFile filename))+ ls <- runIO (fmap lines (readUTF8File filename)) let prefixes = map (takeWhile (\c -> isAlphaNum c || c == '_') . dropWhile (\c -> isSpace c || c == '>')) ls idents = nubBy (\x y -> snd x == snd y) (filter (("prop_" `isPrefixOf`) . snd) (zip [1..] prefixes))+#if __GLASGOW_HASKELL__ > 705+ warning x = reportWarning ("Name " ++ x ++ " found in source file but was not in scope")+#else+ warning x = report False ("Name " ++ x ++ " found in source file but was not in scope")+#endif quickCheckOne :: (Int, String) -> Q [Exp] quickCheckOne (l, x) = do- exists <- return False `recover` (reify (mkName x) >> return True)+ exists <- (warning x >> return False) `recover` (reify (mkName x) >> return True) if exists then sequence [ [| ($(stringE $ x ++ " from " ++ filename ++ ":" ++ show l), property $(monomorphic (mkName x))) |] ] else return [] [| runQuickCheckAll $(fmap (ListE . concat) (mapM quickCheckOne idents)) |] +readUTF8File name = S.openFile name S.ReadMode >>=+ set_utf8_io_enc >>=+ S.hGetContents++-- Deal with UTF-8 input and output.+set_utf8_io_enc :: S.Handle -> IO S.Handle+#if __GLASGOW_HASKELL__ > 611+-- possibly if MIN_VERSION_base(4,2,0)+set_utf8_io_enc h = do S.hSetEncoding h S.utf8; return h+#else+set_utf8_io_enc h = return h+#endif+ -- | Test all properties in the current module. -- The name of the property must begin with @prop_@. -- Polymorphic properties will be defaulted to 'Integer'. -- Returns 'True' if all tests succeeded, 'False' otherwise. ----- Using 'quickCheckAll' interactively doesn't work.--- Instead, add a definition to your module along the lines of+-- To use 'quickCheckAll', add a definition to your module along+-- the lines of --+-- > return [] -- > runTests = $quickCheckAll -- -- and then execute @runTests@.+--+-- Note: the bizarre @return []@ in the example above is needed on+-- GHC 7.8; without it, 'quickCheckAll' will not be able to find+-- any of the properties. For the curious, the @return []@ is a+-- Template Haskell splice that makes GHC insert the empty list+-- of declarations at that point in the program; GHC typechecks+-- everything before the @return []@ before it starts on the rest+-- of the module, which means that the later call to 'quickCheckAll'+-- can see everything that was defined before the @return []@. Yikes! quickCheckAll :: Q Exp quickCheckAll = [| $(forAllProperties) quickCheckResult |] -- | Test all properties in the current module. -- This is just a convenience function that combines 'quickCheckAll' and 'verbose'.+--+-- 'verboseCheckAll' has the same issue with scoping as 'quickCheckAll':+-- see the note there about @return []@. verboseCheckAll :: Q Exp verboseCheckAll = [| $(forAllProperties) verboseCheckResult |]
Test/QuickCheck/Arbitrary.hs view
@@ -10,7 +10,7 @@ , CoArbitrary(..) -- ** Helper functions for implementing arbitrary- , arbitrarySizedIntegral -- :: Num a => Gen a+ , arbitrarySizedIntegral -- :: Integral a => Gen a , arbitraryBoundedIntegral -- :: (Bounded a, Integral a) => Gen a , arbitrarySizedBoundedIntegral -- :: (Bounded a, Integral a) => Gen a , arbitrarySizedFractional -- :: Fractional a => Gen a@@ -436,16 +436,10 @@ arbitrarySizedIntegral :: Integral a => Gen a arbitrarySizedIntegral = sized $ \n ->- withoutOverflow (toInteger n) $ \n' _ ->- withoutOverflow (negate (toInteger n)) $ \m' _ ->- fmap fromInteger (choose (m', n'))+ inBounds fromInteger (choose (-toInteger n, toInteger n)) -withoutOverflow :: Integral a => Integer -> (Integer -> a -> Gen a) -> Gen a-withoutOverflow n k- | toInteger n' == n = k n n'- | otherwise = k 0 0- where- n' = fromInteger n+inBounds :: Integral a => (Integer -> a) -> Gen Integer -> Gen a+inBounds fi g = fmap fi (g `suchThat` (\x -> toInteger (fi x) == x)) -- | Generates a fractional number. The number can be positive or negative -- and its maximum absolute value depends on the size parameter.
Test/QuickCheck/Modifiers.hs view
@@ -145,10 +145,33 @@ newtype Positive a = Positive {getPositive :: a} deriving ( Eq, Ord, Show, Read #ifndef NO_NEWTYPE_DERIVING- , Num, Integral, Real, Enum+ , Integral, Real #endif ) +instance (Ord a, Num a) => Num (Positive a) where+ Positive x + Positive y = positive (x+y)+ Positive x - Positive y = positive (x-y)+ Positive x * Positive y = positive (x*y)+ negate (Positive x) = positive (negate x)+ abs (Positive x) = positive (abs x)+ signum (Positive x) = positive (signum x)+ fromInteger n = positive (clamp (> 0) 1 (fromInteger n))++clamp :: (a -> Bool) -> a -> a -> a+clamp p d x+ | p x = x+ | otherwise = d++positive :: (Ord a, Num a) => a -> Positive a+positive x+ | x <= 0 = error "Positive: negative number"+ | otherwise = Positive x++instance Enum a => Enum (Positive a) where+ toEnum n = fmap toEnum (positive n)+ fromEnum (Positive x) = fromEnum x+ instance Functor Positive where fmap f (Positive x) = Positive (f x) @@ -168,10 +191,27 @@ newtype NonZero a = NonZero {getNonZero :: a} deriving ( Eq, Ord, Show, Read #ifndef NO_NEWTYPE_DERIVING- , Num, Integral, Real, Enum+ , Integral, Real #endif ) +instance (Eq a, Num a) => Num (NonZero a) where+ NonZero x + NonZero y = nonZero (x+y)+ NonZero x - NonZero y = nonZero (x-y)+ NonZero x * NonZero y = nonZero (x*y)+ negate (NonZero x) = nonZero (negate x)+ abs (NonZero x) = nonZero (abs x)+ signum (NonZero x) = nonZero (signum x)+ fromInteger n = nonZero (clamp (/= 0) 0 (fromInteger n))++nonZero :: (Eq a, Num a) => a -> NonZero a+nonZero 0 = error "NonZero: passed in zero"+nonZero x = NonZero x++instance Enum a => Enum (NonZero a) where+ toEnum n = fmap toEnum (nonZero n)+ fromEnum (NonZero x) = fromEnum x+ instance Functor NonZero where fmap f (NonZero x) = NonZero (f x) @@ -185,9 +225,27 @@ newtype NonNegative a = NonNegative {getNonNegative :: a} deriving ( Eq, Ord, Show, Read #ifndef NO_NEWTYPE_DERIVING- , Num, Integral, Real, Enum+ , Integral, Real #endif )++instance (Ord a, Num a) => Num (NonNegative a) where+ NonNegative x + NonNegative y = nonNegative (x+y)+ NonNegative x - NonNegative y = nonNegative (x-y)+ NonNegative x * NonNegative y = nonNegative (x*y)+ negate (NonNegative x) = nonNegative (negate x)+ abs (NonNegative x) = nonNegative (abs x)+ signum (NonNegative x) = nonNegative (signum x)+ fromInteger n = nonNegative (clamp (>= 0) 0 (fromInteger n))++nonNegative :: (Ord a, Num a) => a -> NonNegative a+nonNegative x+ | x < 0 = error "NonNegative: negative number"+ | otherwise = NonNegative x++instance Enum a => Enum (NonNegative a) where+ toEnum n = fmap toEnum (nonNegative n)+ fromEnum (NonNegative x) = fromEnum x instance Functor NonNegative where fmap f (NonNegative x) = NonNegative (f x)
Test/QuickCheck/Monadic.hs view
@@ -19,7 +19,10 @@ import Control.Monad.ST import Control.Applicative --- instance of monad transformer?+#ifndef NO_TRANSFORMERS+import Control.Monad.IO.Class+import Control.Monad.Trans.Class+#endif -------------------------------------------------------------------------- -- type PropertyM@@ -38,6 +41,14 @@ return x = MkPropertyM (\k -> k x) MkPropertyM m >>= f = MkPropertyM (\k -> m (\a -> unPropertyM (f a) k)) fail s = stop (failed { reason = s })++#ifndef NO_TRANSFORMERS+instance MonadTrans PropertyM where+ lift = run++instance MonadIO m => MonadIO (PropertyM m) where+ liftIO = run . liftIO+#endif stop :: (Testable prop, Monad m) => prop -> PropertyM m a stop p = MkPropertyM (\_k -> return (return (property p)))
Test/QuickCheck/Property.hs view
@@ -63,6 +63,16 @@ -- * Property and Testable types -- | The type of properties.+--+-- Backwards combatibility note: in older versions of QuickCheck+-- 'Property' was a type synonym for @'Gen' 'Prop'@, so you could mix+-- and match property combinators and 'Gen' monad operations. Code+-- that does this will no longer typecheck.+-- However, it is easy to fix: because of the 'Testable' typeclass, any+-- combinator that expects a 'Property' will also accept a @'Gen' 'Property'@.+-- If you have a 'Property' where you need a @'Gen' 'a'@, simply wrap+-- the property combinator inside a 'return' to get a @'Gen' 'Property'@, and+-- all should be well. newtype Property = MkProperty { unProperty :: Gen Prop } -- | The class of things which can be tested, i.e. turned into a property.@@ -98,7 +108,7 @@ property mp = MkProperty $ do p <- mp; unProperty (property p) instance Testable Property where- property = id+ property = property . unProperty -- | Do I/O inside a property. This can obviously lead to unrepeatable -- testcases, so use with care.
examples/Heap.hs view
@@ -145,6 +145,9 @@ -------------------------------------------------------------------------- -- main +return []+-- quickCheckAll reads this file and treats it as UTF-8+-- Here is a bait to test: Привет! main = $(quickCheckAll) --------------------------------------------------------------------------