QuickCheck 2.7.6 → 2.8
raw patch · 16 files changed
+693/−234 lines, 16 filesdep +containersdep ~QuickCheckdep ~basePVP ok
version bump matches the API change (PVP)
Dependencies added: containers
Dependency ranges changed: QuickCheck, base
API changes (from Hackage documentation)
- Test.QuickCheck.Arbitrary: instance (Arbitrary a, Arbitrary b) => Arbitrary (Either a b)
- Test.QuickCheck.Arbitrary: instance (Arbitrary a, Arbitrary b) => Arbitrary (a, b)
- Test.QuickCheck.Arbitrary: instance (Arbitrary a, Arbitrary b, Arbitrary c) => Arbitrary (a, b, c)
- Test.QuickCheck.Arbitrary: instance (Arbitrary a, Arbitrary b, Arbitrary c, Arbitrary d) => Arbitrary (a, b, c, d)
- Test.QuickCheck.Arbitrary: instance (Arbitrary a, Arbitrary b, Arbitrary c, Arbitrary d, Arbitrary e) => Arbitrary (a, b, c, d, e)
- Test.QuickCheck.Arbitrary: instance (Arbitrary a, CoArbitrary b) => CoArbitrary (a -> b)
- Test.QuickCheck.Arbitrary: instance (CoArbitrary a, Arbitrary b) => Arbitrary (a -> b)
- Test.QuickCheck.Arbitrary: instance (CoArbitrary a, CoArbitrary b) => CoArbitrary (Either a b)
- Test.QuickCheck.Arbitrary: instance (CoArbitrary a, CoArbitrary b) => CoArbitrary (a, b)
- Test.QuickCheck.Arbitrary: instance (CoArbitrary a, CoArbitrary b, CoArbitrary c) => CoArbitrary (a, b, c)
- Test.QuickCheck.Arbitrary: instance (CoArbitrary a, CoArbitrary b, CoArbitrary c, CoArbitrary d) => CoArbitrary (a, b, c, d)
- Test.QuickCheck.Arbitrary: instance (CoArbitrary a, CoArbitrary b, CoArbitrary c, CoArbitrary d, CoArbitrary e) => CoArbitrary (a, b, c, d, e)
- Test.QuickCheck.Arbitrary: instance (Integral a, Arbitrary a) => Arbitrary (Ratio a)
- Test.QuickCheck.Arbitrary: instance (Integral a, CoArbitrary a) => CoArbitrary (Ratio a)
- Test.QuickCheck.Arbitrary: instance (RealFloat a, Arbitrary a) => Arbitrary (Complex a)
- Test.QuickCheck.Arbitrary: instance (RealFloat a, CoArbitrary a) => CoArbitrary (Complex a)
- Test.QuickCheck.Arbitrary: instance (RecursivelyShrink f, RecursivelyShrink g) => RecursivelyShrink (f :*: g)
- Test.QuickCheck.Arbitrary: instance (RecursivelyShrink f, RecursivelyShrink g) => RecursivelyShrink (f :+: g)
- Test.QuickCheck.Arbitrary: instance (Subterms f, Subterms g) => Subterms (f :*: g)
- Test.QuickCheck.Arbitrary: instance (Subterms f, Subterms g) => Subterms (f :+: g)
- Test.QuickCheck.Arbitrary: instance Arbitrary ()
- Test.QuickCheck.Arbitrary: instance Arbitrary Bool
- Test.QuickCheck.Arbitrary: instance Arbitrary Char
- Test.QuickCheck.Arbitrary: instance Arbitrary Double
- Test.QuickCheck.Arbitrary: instance Arbitrary Float
- Test.QuickCheck.Arbitrary: instance Arbitrary Int
- Test.QuickCheck.Arbitrary: instance Arbitrary Int16
- Test.QuickCheck.Arbitrary: instance Arbitrary Int32
- Test.QuickCheck.Arbitrary: instance Arbitrary Int64
- Test.QuickCheck.Arbitrary: instance Arbitrary Int8
- Test.QuickCheck.Arbitrary: instance Arbitrary Integer
- Test.QuickCheck.Arbitrary: instance Arbitrary Ordering
- Test.QuickCheck.Arbitrary: instance Arbitrary Word
- Test.QuickCheck.Arbitrary: instance Arbitrary Word16
- Test.QuickCheck.Arbitrary: instance Arbitrary Word32
- Test.QuickCheck.Arbitrary: instance Arbitrary Word64
- Test.QuickCheck.Arbitrary: instance Arbitrary Word8
- Test.QuickCheck.Arbitrary: instance Arbitrary a => Arbitrary (Maybe a)
- Test.QuickCheck.Arbitrary: instance Arbitrary a => Arbitrary [a]
- Test.QuickCheck.Arbitrary: instance Arbitrary a => RecursivelyShrink (K1 i a)
- Test.QuickCheck.Arbitrary: instance CoArbitrary ()
- Test.QuickCheck.Arbitrary: instance CoArbitrary Bool
- Test.QuickCheck.Arbitrary: instance CoArbitrary Char
- Test.QuickCheck.Arbitrary: instance CoArbitrary Double
- Test.QuickCheck.Arbitrary: instance CoArbitrary Float
- Test.QuickCheck.Arbitrary: instance CoArbitrary Int
- Test.QuickCheck.Arbitrary: instance CoArbitrary Int16
- Test.QuickCheck.Arbitrary: instance CoArbitrary Int32
- Test.QuickCheck.Arbitrary: instance CoArbitrary Int64
- Test.QuickCheck.Arbitrary: instance CoArbitrary Int8
- Test.QuickCheck.Arbitrary: instance CoArbitrary Integer
- Test.QuickCheck.Arbitrary: instance CoArbitrary Ordering
- Test.QuickCheck.Arbitrary: instance CoArbitrary Word
- Test.QuickCheck.Arbitrary: instance CoArbitrary Word16
- Test.QuickCheck.Arbitrary: instance CoArbitrary Word32
- Test.QuickCheck.Arbitrary: instance CoArbitrary Word64
- Test.QuickCheck.Arbitrary: instance CoArbitrary Word8
- Test.QuickCheck.Arbitrary: instance CoArbitrary a => CoArbitrary (Maybe a)
- Test.QuickCheck.Arbitrary: instance CoArbitrary a => CoArbitrary [a]
- Test.QuickCheck.Arbitrary: instance HasResolution a => Arbitrary (Fixed a)
- Test.QuickCheck.Arbitrary: instance HasResolution a => CoArbitrary (Fixed a)
- Test.QuickCheck.Arbitrary: instance RecursivelyShrink U1
- Test.QuickCheck.Arbitrary: instance RecursivelyShrink f => RecursivelyShrink (M1 i c f)
- Test.QuickCheck.Arbitrary: instance Subterms U1
- Test.QuickCheck.Arbitrary: instance Subterms f => Subterms (M1 i c f)
- Test.QuickCheck.Arbitrary: instance Typeable a => Subterms (K1 i a)
- Test.QuickCheck.Test: safely :: State -> IO () -> IO ()
+ Test.QuickCheck: InsufficientCoverage :: Int -> [(String, Int)] -> String -> Result
+ Test.QuickCheck: arbitrarySizedNatural :: Integral a => Gen a
+ Test.QuickCheck: genericCoarbitrary :: (Generic a, GCoArbitrary (Rep a)) => a -> Gen b -> Gen b
+ Test.QuickCheck: scale :: (Int -> Int) -> Gen a -> Gen a
+ Test.QuickCheck: shuffle :: [a] -> Gen [a]
+ Test.QuickCheck: sublistOf :: [a] -> Gen [a]
+ Test.QuickCheck.Arbitrary: arbitrarySizedNatural :: Integral a => Gen a
+ Test.QuickCheck.Arbitrary: genericCoarbitrary :: (Generic a, GCoArbitrary (Rep a)) => a -> Gen b -> Gen b
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] (Arbitrary a, Arbitrary b) => Arbitrary (Either a b)
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] (Arbitrary a, Arbitrary b) => Arbitrary (a, b)
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] (Arbitrary a, Arbitrary b, Arbitrary c) => Arbitrary (a, b, c)
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] (Arbitrary a, Arbitrary b, Arbitrary c, Arbitrary d) => Arbitrary (a, b, c, d)
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] (Arbitrary a, Arbitrary b, Arbitrary c, Arbitrary d, Arbitrary e) => Arbitrary (a, b, c, d, e)
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] (Arbitrary a, CoArbitrary b) => CoArbitrary (a -> b)
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] (CoArbitrary a, Arbitrary b) => Arbitrary (a -> b)
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] (CoArbitrary a, CoArbitrary b) => CoArbitrary (Either a b)
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] (CoArbitrary a, CoArbitrary b) => CoArbitrary (a, b)
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] (CoArbitrary a, CoArbitrary b, CoArbitrary c) => CoArbitrary (a, b, c)
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] (CoArbitrary a, CoArbitrary b, CoArbitrary c, CoArbitrary d) => CoArbitrary (a, b, c, d)
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] (CoArbitrary a, CoArbitrary b, CoArbitrary c, CoArbitrary d, CoArbitrary e) => CoArbitrary (a, b, c, d, e)
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] (GCoArbitrary f, GCoArbitrary g) => GCoArbitrary (f :*: g)
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] (GCoArbitrary f, GCoArbitrary g) => GCoArbitrary (f :+: g)
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] (GSubtermsIncl f a, GSubtermsIncl g a) => GSubterms (f :*: g) a
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] (GSubtermsIncl f a, GSubtermsIncl g a) => GSubterms (f :+: g) a
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] (GSubtermsIncl f a, GSubtermsIncl g a) => GSubtermsIncl (f :*: g) a
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] (GSubtermsIncl f a, GSubtermsIncl g a) => GSubtermsIncl (f :+: g) a
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] (Integral a, Arbitrary a) => Arbitrary (Ratio a)
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] (Integral a, CoArbitrary a) => CoArbitrary (Ratio a)
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] (RealFloat a, Arbitrary a) => Arbitrary (Complex a)
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] (RealFloat a, CoArbitrary a) => CoArbitrary (Complex a)
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] (RecursivelyShrink f, RecursivelyShrink g) => RecursivelyShrink (f :*: g)
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] (RecursivelyShrink f, RecursivelyShrink g) => RecursivelyShrink (f :+: g)
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] Arbitrary ()
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] Arbitrary Bool
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] Arbitrary Char
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] Arbitrary Double
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] Arbitrary Float
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] Arbitrary Int
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] Arbitrary Int16
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] Arbitrary Int32
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] Arbitrary Int64
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] Arbitrary Int8
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] Arbitrary Integer
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] Arbitrary Ordering
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] Arbitrary Word
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] Arbitrary Word16
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] Arbitrary Word32
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] Arbitrary Word64
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] Arbitrary Word8
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] Arbitrary a => Arbitrary (Maybe a)
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] Arbitrary a => Arbitrary [a]
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] Arbitrary a => GSubtermsIncl (K1 i a) a
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] Arbitrary a => RecursivelyShrink (K1 i a)
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] CoArbitrary ()
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] CoArbitrary Bool
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] CoArbitrary Char
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] CoArbitrary Double
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] CoArbitrary Float
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] CoArbitrary Int
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] CoArbitrary Int16
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] CoArbitrary Int32
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] CoArbitrary Int64
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] CoArbitrary Int8
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] CoArbitrary Integer
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] CoArbitrary Ordering
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] CoArbitrary Word
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] CoArbitrary Word16
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] CoArbitrary Word32
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] CoArbitrary Word64
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] CoArbitrary Word8
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] CoArbitrary a => CoArbitrary (Maybe a)
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] CoArbitrary a => CoArbitrary [a]
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] CoArbitrary a => GCoArbitrary (K1 i a)
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] GCoArbitrary U1
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] GCoArbitrary f => GCoArbitrary (M1 i c f)
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] GSubterms (K1 i a) b
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] GSubterms U1 a
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] GSubterms V1 a
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] GSubterms f a => GSubterms (M1 i c f) a
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] GSubtermsIncl (K1 i a) b
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] GSubtermsIncl U1 a
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] GSubtermsIncl V1 a
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] GSubtermsIncl f a => GSubtermsIncl (M1 i c f) a
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] HasResolution a => Arbitrary (Fixed a)
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] HasResolution a => CoArbitrary (Fixed a)
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] RecursivelyShrink U1
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] RecursivelyShrink V1
+ Test.QuickCheck.Arbitrary: instance [overlap ok] [safe] RecursivelyShrink f => RecursivelyShrink (M1 i c f)
+ Test.QuickCheck.Function: instance (Function a, Integral a) => Function (Ratio a)
+ Test.QuickCheck.Gen: scale :: (Int -> Int) -> Gen a -> Gen a
+ Test.QuickCheck.Gen: shuffle :: [a] -> Gen [a]
+ Test.QuickCheck.Gen: sublistOf :: [a] -> Gen [a]
+ Test.QuickCheck.Property: labels :: Result -> Map String Int
+ Test.QuickCheck.State: labels :: State -> !(Map String Int)
+ Test.QuickCheck.Test: InsufficientCoverage :: Int -> [(String, Int)] -> String -> Result
+ Test.QuickCheck.Test: insufficientCoverage :: State -> Bool
+ Test.QuickCheck.Test: labelPercentage :: String -> State -> Int
- Test.QuickCheck: class CoArbitrary a
+ Test.QuickCheck: class CoArbitrary a where coarbitrary = genericCoarbitrary
- Test.QuickCheck: genericShrink :: (Generic a, Typeable a, RecursivelyShrink (Rep a), Subterms (Rep a)) => a -> [a]
+ Test.QuickCheck: genericShrink :: (Generic a, Arbitrary a, RecursivelyShrink (Rep a), GSubterms (Rep a) a) => a -> [a]
- Test.QuickCheck: subterms :: (Generic a, Typeable a, Subterms (Rep a)) => a -> [a]
+ Test.QuickCheck: subterms :: (Generic a, Arbitrary a, GSubterms (Rep a) a) => a -> [a]
- Test.QuickCheck.Arbitrary: class CoArbitrary a
+ Test.QuickCheck.Arbitrary: class CoArbitrary a where coarbitrary = genericCoarbitrary
- Test.QuickCheck.Arbitrary: genericShrink :: (Generic a, Typeable a, RecursivelyShrink (Rep a), Subterms (Rep a)) => a -> [a]
+ Test.QuickCheck.Arbitrary: genericShrink :: (Generic a, Arbitrary a, RecursivelyShrink (Rep a), GSubterms (Rep a) a) => a -> [a]
- Test.QuickCheck.Arbitrary: subterms :: (Generic a, Typeable a, Subterms (Rep a)) => a -> [a]
+ Test.QuickCheck.Arbitrary: subterms :: (Generic a, Arbitrary a, GSubterms (Rep a) a) => a -> [a]
- Test.QuickCheck.Property: MkResult :: Maybe Bool -> Bool -> String -> Maybe AnException -> Bool -> [(String, Int)] -> [Callback] -> Result
+ Test.QuickCheck.Property: MkResult :: Maybe Bool -> Bool -> String -> Maybe AnException -> Bool -> Map String Int -> Set String -> [Callback] -> Result
- Test.QuickCheck.Property: stamp :: Result -> [(String, Int)]
+ Test.QuickCheck.Property: stamp :: Result -> Set String
- Test.QuickCheck.State: MkState :: Terminal -> Int -> Int -> (Int -> Int -> Int) -> Int -> Int -> Int -> [[(String, Int)]] -> Bool -> QCGen -> Int -> Int -> Int -> State
+ Test.QuickCheck.State: MkState :: Terminal -> Int -> Int -> (Int -> Int -> Int) -> !Int -> !Int -> !Int -> !(Map String Int) -> ![Set String] -> !Bool -> !QCGen -> !Int -> !Int -> !Int -> State
- Test.QuickCheck.State: collected :: State -> [[(String, Int)]]
+ Test.QuickCheck.State: collected :: State -> ![Set String]
- Test.QuickCheck.State: expectedFailure :: State -> Bool
+ Test.QuickCheck.State: expectedFailure :: State -> !Bool
- Test.QuickCheck.State: numDiscardedTests :: State -> Int
+ Test.QuickCheck.State: numDiscardedTests :: State -> !Int
- Test.QuickCheck.State: numRecentlyDiscardedTests :: State -> Int
+ Test.QuickCheck.State: numRecentlyDiscardedTests :: State -> !Int
- Test.QuickCheck.State: numSuccessShrinks :: State -> Int
+ Test.QuickCheck.State: numSuccessShrinks :: State -> !Int
- Test.QuickCheck.State: numSuccessTests :: State -> Int
+ Test.QuickCheck.State: numSuccessTests :: State -> !Int
- Test.QuickCheck.State: numTotTryShrinks :: State -> Int
+ Test.QuickCheck.State: numTotTryShrinks :: State -> !Int
- Test.QuickCheck.State: numTryShrinks :: State -> Int
+ Test.QuickCheck.State: numTryShrinks :: State -> !Int
- Test.QuickCheck.State: randomSeed :: State -> QCGen
+ Test.QuickCheck.State: randomSeed :: State -> !QCGen
- Test.QuickCheck.Test: callbackPostTest :: State -> Result -> IO ()
+ Test.QuickCheck.Test: callbackPostTest :: State -> Result -> IO Result
- Test.QuickCheck.Text: newTerminal :: Output -> Output -> IO Terminal
+ Test.QuickCheck.Text: newTerminal :: (String -> IO ()) -> (String -> IO ()) -> IO Terminal
Files
- LICENSE +2/−2
- QuickCheck.cabal +33/−11
- Test/QuickCheck.hs +72/−0
- Test/QuickCheck/All.hs +12/−2
- Test/QuickCheck/Arbitrary.hs +148/−25
- Test/QuickCheck/Exception.hs +1/−1
- Test/QuickCheck/Function.hs +24/−2
- Test/QuickCheck/Gen.hs +29/−2
- Test/QuickCheck/Gen/Unsafe.hs +1/−1
- Test/QuickCheck/Monadic.hs +151/−7
- Test/QuickCheck/Property.hs +56/−33
- Test/QuickCheck/State.hs +15/−11
- Test/QuickCheck/Test.hs +102/−83
- Test/QuickCheck/Text.hs +21/−49
- changelog +21/−0
- examples/Heap.hs +5/−5
LICENSE view
@@ -1,6 +1,6 @@-Copyright (c) 2000-2014, Koen Claessen+Copyright (c) 2000-2015, Koen Claessen Copyright (c) 2006-2008, Björn Bringert-Copyright (c) 2009-2014, Nick Smallbone+Copyright (c) 2009-2015, Nick Smallbone All rights reserved. Redistribution and use in source and binary forms, with or without
QuickCheck.cabal view
@@ -1,15 +1,15 @@ Name: QuickCheck-Version: 2.7.6+Version: 2.8 Cabal-Version: >= 1.8 Build-type: Simple License: BSD3 License-file: LICENSE Extra-source-files: README changelog-Copyright: 2000-2014 Koen Claessen, 2006-2008 Björn Bringert, 2009-2014 Nick Smallbone+Copyright: 2000-2015 Koen Claessen, 2006-2008 Björn Bringert, 2009-2015 Nick Smallbone Author: Koen Claessen <koen@chalmers.se> Maintainer: QuickCheck developers <quickcheck@projects.haskell.org> Bug-reports: mailto:quickcheck@projects.haskell.org-Tested-with: GHC >=6.8, Hugs, UHC+Tested-with: GHC >=6.10, Hugs, UHC Homepage: https://github.com/nick8325/quickcheck Category: Testing Synopsis: Automatic testing of Haskell programs@@ -26,6 +26,9 @@ QuickCheck provides combinators to define properties, observe the distribution of test data, and define test data generators.+ .+ You can find a (slightly out-of-date but useful) manual at+ <http://www.cse.chalmers.se/~rjmh/QuickCheck/manual.html>. source-repository head type: git@@ -34,7 +37,7 @@ source-repository this type: git location: https://github.com/nick8325/quickcheck- tag: 2.7.6+ tag: 2.8 flag base3 Description: Choose the new smaller, split-up base package.@@ -42,18 +45,27 @@ flag base4 Description: Choose the even newer base package with extensible exceptions. +flag base4point8+ Description: Choose the even more newer base package with natural numbers.+ flag templateHaskell Description: Build Test.QuickCheck.All, which uses Template Haskell. library -- Choose which library versions to use.- if flag(base4)- Build-depends: base >= 4 && < 5, random+ if flag(base4point8)+ Build-depends: base >= 4.8 && < 5 else- if flag(base3)- Build-depends: base >= 3 && < 4, random+ if flag(base4)+ Build-depends: base >= 4 && < 4.8 else- Build-depends: base < 3+ if flag(base3)+ Build-depends: base >= 3 && < 4+ else+ Build-depends: base < 3+ if flag(base4point8) || flag(base4) || flag(base3)+ Build-depends: random+ Build-depends: containers -- Modules that are always built. Exposed-Modules:@@ -110,11 +122,15 @@ cpp-options: -DNO_SAFE_HASKELL -- Use tf-random on newer GHCs.- if impl(ghc >= 7) && flag(base4)+ if impl(ghc >= 7) && (flag(base4point8) || flag(base4)) Build-depends: tf-random >= 0.4 else cpp-options: -DNO_TF_RANDOM + -- Natural numbers.+ if !flag(base4point8)+ cpp-options: -DNO_NATURALS+ -- Switch off most optional features on non-GHC systems. if !impl(ghc) -- If your Haskell compiler can cope without some of these, please@@ -141,6 +157,12 @@ main-is: Heap.hs build-depends: base,- QuickCheck == 2.7.6,+ containers,+ QuickCheck == 2.8, template-haskell >= 2.4, test-framework >= 0.4 && < 0.9+ if flag(templateHaskell)+ Buildable: True+ else+ Buildable: False+
Test/QuickCheck.hs view
@@ -1,3 +1,70 @@+{-| For further information see the <http://www.cse.chalmers.se/~rjmh/QuickCheck/manual.html QuickCheck manual>.++To use QuickCheck to check a property, first define a function+expressing that property (functions expressing properties under test+tend to be prefixed with @prop_@). Testing that @n + m = m + n@ holds+for @Integer@s one might write:++@+import Test.QuickCheck++prop_commutativeAdd :: Integer -> Integer -> Bool+prop_commutativeAdd n m = n + m == m + n+@++and testing:++>>> quickcheck prop_commutativeAdd++++ OK, passed 100 tests.++which tests @prop_commutativeAdd@ on 100 random @(Integer, Integer)@ pairs.++'verboseCheck' can be used to see the actual values generated:++>>> verboseCheck prop_commutativeAdd+Passed:+0+0+ …98 tests omitted…+Passed:+-68+6++++ OK, passed 100 tests.++and if more than 100 tests are needed the number of tests can be+increased by updating the 'stdArgs' record:++>>> quickCheckWith stdArgs { maxSuccess = 500 } prop_commutativeAdd++++ OK, passed 500 tests.++To let QuickCheck generate values of your own data type an 'Arbitrary'+instance must be defined:++@+data Point = MkPoint Int Int deriving Eq++instance Arbitrary Point where+ arbitrary = do+ x <- 'arbitrary'+ y <- arbitrary+ return (MkPoint x y)++swapPoint :: Point -> Point+swapPoint (MkPoint x y) = MkPoint y x++-- swapPoint . swapPoint = id+prop_swapInvolution point = swapPoint (swapPoint point) == point+@++>>> quickCheck prop_swapInvolution++++ OK, passed 100 tests.++See "Test.QuickCheck.Function" for generating random shrinkable,+showable functions used for testing higher-order functions and+"Test.QuickCheck.Monadic" for testing impure or monadic code+(e.g. effectful code in 'IO').++-} {-# LANGUAGE CPP #-} #ifndef NO_SAFE_HASKELL {-# LANGUAGE Safe #-}@@ -37,12 +104,15 @@ , growingElements , sized , resize+ , scale , suchThat , suchThatMaybe , listOf , listOf1 , vectorOf , infiniteListOf+ , shuffle+ , sublistOf -- ** Generators which use Arbitrary , vector , orderedList@@ -59,6 +129,7 @@ -- ** Helper functions for implementing arbitrary , arbitrarySizedIntegral+ , arbitrarySizedNatural , arbitrarySizedFractional , arbitrarySizedBoundedIntegral , arbitraryBoundedIntegral@@ -66,6 +137,7 @@ , arbitraryBoundedEnum -- ** Helper functions for implementing shrink #ifndef NO_GENERICS+ , genericCoarbitrary , genericShrink , subterms , recursivelyShrink
Test/QuickCheck/All.hs view
@@ -79,8 +79,17 @@ deconstructType :: Error -> Type -> Q ([Name], Cxt, Type) deconstructType err ty0@(ForallT xs ctx ty) = do- let plain (PlainTV _) = True- plain _ = False+ let plain (PlainTV _) = True+#ifndef MIN_VERSION_template_haskell+ plain (KindedTV _ StarT) = True+#else+#if MIN_VERSION_template_haskell(2,8,0)+ plain (KindedTV _ StarT) = True+#else+ plain (KindedTV _ StarK) = True+#endif+#endif+ plain _ = False unless (all plain xs) $ err "Higher-kinded type variables in type" return (map (\(PlainTV x) -> x) xs, ctx, ty) deconstructType _ ty = return ([], [], ty)@@ -177,3 +186,4 @@ Failure { } -> False NoExpectedFailure { } -> False GaveUp { } -> False+ InsufficientCoverage { } -> False
Test/QuickCheck/Arbitrary.hs view
@@ -2,7 +2,12 @@ {-# LANGUAGE CPP #-} #ifndef NO_GENERICS {-# LANGUAGE DefaultSignatures, FlexibleContexts, TypeOperators #-}+{-# LANGUAGE FlexibleInstances, KindSignatures, ScopedTypeVariables #-}+{-# LANGUAGE MultiParamTypeClasses, OverlappingInstances #-} #endif+#ifndef NO_SAFE_HASKELL+{-# LANGUAGE Safe #-}+#endif module Test.QuickCheck.Arbitrary ( -- * Arbitrary and CoArbitrary classes@@ -11,6 +16,7 @@ -- ** Helper functions for implementing arbitrary , arbitrarySizedIntegral -- :: Integral a => Gen a+ , arbitrarySizedNatural -- :: Integral a => Gen a , arbitraryBoundedIntegral -- :: (Bounded a, Integral a) => Gen a , arbitrarySizedBoundedIntegral -- :: (Bounded a, Integral a) => Gen a , arbitrarySizedFractional -- :: Fractional a => Gen a@@ -18,9 +24,10 @@ , arbitraryBoundedEnum -- :: (Bounded a, Enum a) => Gen a -- ** Helper functions for implementing shrink #ifndef NO_GENERICS- , genericShrink -- :: (Generic a, Typeable a, RecursivelyShrink (Rep a), Subterms (Rep a)) => a -> [a]- , subterms -- :: (Generic a, Subterms (Rep a)) => a -> [a]+ , genericShrink -- :: (Generic a, Arbitrary a, RecursivelyShrink (Rep a), GSubterms (Rep a) a) => a -> [a]+ , subterms -- :: (Generic a, Arbitrary a, GSubterms (Rep a) a) => a -> [a] , recursivelyShrink -- :: (Generic a, RecursivelyShrink (Rep a)) => a -> [a]+ , genericCoarbitrary -- :: (Generic a, GCoArbitrary (Rep a)) => a -> Gen b -> Gen b #endif , shrinkNothing -- :: a -> [a] , shrinkList -- :: (a -> [a]) -> [a] -> [[a]]@@ -44,6 +51,7 @@ -------------------------------------------------------------------------- -- imports +import Control.Applicative import System.Random(Random) import Test.QuickCheck.Gen import Test.QuickCheck.Gen.Unsafe@@ -73,6 +81,10 @@ ) #endif +#ifndef NO_NATURALS+import Numeric.Natural+#endif+ import Data.Ratio ( Ratio , (%)@@ -101,7 +113,6 @@ #ifndef NO_GENERICS import GHC.Generics-import Data.Typeable #endif --------------------------------------------------------------------------@@ -176,7 +187,7 @@ -- infinite loop. -- -- If all this leaves you bewildered, you might try @'shrink' = 'genericShrink'@ to begin with,- -- after deriving @Generic@ and @Typeable@ for your type. However, if your data type has any+ -- after deriving @Generic@ for your type. However, if your data type has any -- special invariants, you will need to check that 'genericShrink' can't break those invariants. shrink :: a -> [a] shrink _ = []@@ -184,7 +195,7 @@ #ifndef NO_GENERICS -- | Shrink a term to any of its immediate subterms, -- and also recursively shrink all subterms.-genericShrink :: (Generic a, Typeable a, RecursivelyShrink (Rep a), Subterms (Rep a)) => a -> [a]+genericShrink :: (Generic a, Arbitrary a, RecursivelyShrink (Rep a), GSubterms (Rep a) a) => a -> [a] genericShrink x = subterms x ++ recursivelyShrink x -- | Recursively shrink all immediate subterms.@@ -212,33 +223,81 @@ instance RecursivelyShrink U1 where grecursivelyShrink U1 = [] +instance RecursivelyShrink V1 where+ -- The empty type can't be shrunk to anything.+ grecursivelyShrink _ = []++ -- | All immediate subterms of a term.-subterms :: (Generic a, Typeable a, Subterms (Rep a)) => a -> [a]-subterms = gsubterms . from+subterms :: (Generic a, Arbitrary a, GSubterms (Rep a) a) => a -> [a]+subterms = gSubterms . from -class Subterms f where- gsubterms :: Typeable b => f a -> [b] -instance (Subterms f, Subterms g) => Subterms (f :*: g) where- gsubterms (x :*: y) =- gsubterms x ++ gsubterms y+class GSubterms f a where+ -- | Provides the immediate subterms of a term that are of the same type+ -- as the term itself.+ --+ -- Requires a constructor to be stripped off; this means it skips through+ -- @M1@ wrappers and returns @[]@ on everything that's not `(:*:)` or `(:+:)`.+ --+ -- Once a `(:*:)` or `(:+:)` constructor has been reached, this function+ -- delegates to `gSubtermsIncl` to return the immediately next constructor+ -- available.+ gSubterms :: f a -> [a] -instance (Subterms f, Subterms g) => Subterms (f :+: g) where- gsubterms (L1 x) = gsubterms x- gsubterms (R1 x) = gsubterms x+instance GSubterms V1 a where+ -- The empty type can't be shrunk to anything.+ gSubterms _ = [] -instance Subterms f => Subterms (M1 i c f) where- gsubterms (M1 x) = gsubterms x+instance GSubterms U1 a where+ gSubterms U1 = [] -instance Typeable a => Subterms (K1 i a) where- gsubterms (K1 x) =- case cast x of- Nothing -> []- Just y -> [y]+instance (GSubtermsIncl f a, GSubtermsIncl g a) => GSubterms (f :*: g) a where+ gSubterms (l :*: r) = gSubtermsIncl l ++ gSubtermsIncl r -instance Subterms U1 where- gsubterms U1 = []+instance (GSubtermsIncl f a, GSubtermsIncl g a) => GSubterms (f :+: g) a where+ gSubterms (L1 x) = gSubtermsIncl x+ gSubterms (R1 x) = gSubtermsIncl x +instance GSubterms f a => GSubterms (M1 i c f) a where+ gSubterms (M1 x) = gSubterms x++instance GSubterms (K1 i a) b where+ gSubterms (K1 _) = []+++class GSubtermsIncl f a where+ -- | Provides the immediate subterms of a term that are of the same type+ -- as the term itself.+ --+ -- In contrast to `gSubterms`, this returns the immediate next constructor+ -- available.+ gSubtermsIncl :: f a -> [a]++instance GSubtermsIncl V1 a where+ -- The empty type can't be shrunk to anything.+ gSubtermsIncl _ = []++instance GSubtermsIncl U1 a where+ gSubtermsIncl U1 = []++instance (GSubtermsIncl f a, GSubtermsIncl g a) => GSubtermsIncl (f :*: g) a where+ gSubtermsIncl (l :*: r) = gSubtermsIncl l ++ gSubtermsIncl r++instance (GSubtermsIncl f a, GSubtermsIncl g a) => GSubtermsIncl (f :+: g) a where+ gSubtermsIncl (L1 x) = gSubtermsIncl x+ gSubtermsIncl (R1 x) = gSubtermsIncl x++instance GSubtermsIncl f a => GSubtermsIncl (M1 i c f) a where+ gSubtermsIncl (M1 x) = gSubtermsIncl x++-- This is the important case: We've found a term of the same type.+instance Arbitrary a => GSubtermsIncl (K1 i a) a where+ gSubtermsIncl (K1 x) = [x]++instance GSubtermsIncl (K1 i a) b where+ gSubtermsIncl (K1 _) = []+ #endif -- instances@@ -363,6 +422,12 @@ arbitrary = arbitrarySizedIntegral shrink = shrinkIntegral +#ifndef NO_NATURALS+instance Arbitrary Natural where+ arbitrary = arbitrarySizedNatural+ shrink = shrinkIntegral+#endif+ instance Arbitrary Int where arbitrary = arbitrarySizedIntegral shrink = shrinkIntegral@@ -438,6 +503,13 @@ sized $ \n -> inBounds fromInteger (choose (-toInteger n, toInteger n)) +-- | Generates a natural number. The number's maximum value depends on+-- the size parameter.+arbitrarySizedNatural :: Integral a => Gen a+arbitrarySizedNatural =+ sized $ \n ->+ inBounds fromInteger (choose (0, toInteger n))+ inBounds :: Integral a => (Integer -> a) -> Gen Integer -> Gen a inBounds fi g = fmap fi (g `suchThat` (\x -> toInteger (fi x) == x)) @@ -540,7 +612,25 @@ -------------------------------------------------------------------------- -- ** CoArbitrary +#ifndef NO_GENERICS -- | Used for random generation of functions.+--+-- If you are using a recent GHC, there is a default definition of+-- 'coarbitrary' using 'genericCoarbitrary', so if your type has a+-- 'Generic' instance it's enough to say+--+-- > instance CoArbitrary MyType+--+-- You should only use 'genericCoarbitrary' for data types where+-- equality is structural, i.e. if you can't have two different+-- representations of the same value. An example where it's not+-- safe is sets implemented using binary search trees: the same+-- set can be represented as several different trees.+-- Here you would have to explicitly define+-- @coarbitrary s = coarbitrary (toList s)@.+#else+-- | Used for random generation of functions.+#endif class CoArbitrary a where -- | Used to generate a function of type @a -> b@. -- The first argument is a value, the second a generator.@@ -553,9 +643,37 @@ -- coarbitrary [] = 'variant' 0 -- coarbitrary (x:xs) = 'variant' 1 . coarbitrary (x,xs) -- @- coarbitrary :: a -> Gen b -> Gen b+#ifndef NO_GENERICS+ default coarbitrary :: (Generic a, GCoArbitrary (Rep a)) => a -> Gen b -> Gen b+ coarbitrary = genericCoarbitrary +-- | Generic CoArbitrary implementation.+genericCoarbitrary :: (Generic a, GCoArbitrary (Rep a)) => a -> Gen b -> Gen b+genericCoarbitrary = gCoarbitrary . from++class GCoArbitrary f where+ gCoarbitrary :: f a -> Gen b -> Gen b++instance GCoArbitrary U1 where+ gCoarbitrary U1 = id++instance (GCoArbitrary f, GCoArbitrary g) => GCoArbitrary (f :*: g) where+ -- Like the instance for tuples.+ gCoarbitrary (l :*: r) = gCoarbitrary l . gCoarbitrary r++instance (GCoArbitrary f, GCoArbitrary g) => GCoArbitrary (f :+: g) where+ -- Like the instance for Either.+ gCoarbitrary (L1 x) = variant 0 . gCoarbitrary x+ gCoarbitrary (R1 x) = variant 1 . gCoarbitrary x++instance GCoArbitrary f => GCoArbitrary (M1 i c f) where+ gCoarbitrary (M1 x) = gCoarbitrary x++instance CoArbitrary a => GCoArbitrary (K1 i a) where+ gCoarbitrary (K1 x) = coarbitrary x+#endif+ {-# DEPRECATED (><) "Use ordinary function composition instead" #-} -- | Combine two generator perturbing functions, for example the -- results of calls to 'variant' or 'coarbitrary'.@@ -636,6 +754,11 @@ instance CoArbitrary Integer where coarbitrary = coarbitraryIntegral++#ifndef NO_NATURALS+instance CoArbitrary Natural where+ coarbitrary = coarbitraryIntegral+#endif instance CoArbitrary Int where coarbitrary = coarbitraryIntegral
Test/QuickCheck/Exception.hs view
@@ -80,7 +80,7 @@ tryEvaluateIO m = E.try (m >>= E.evaluate) --tryEvaluateIO m = Right `fmap` m --- Test if an exception was a ^C.+-- | Test if an exception was a @^C@. -- QuickCheck won't try to shrink an interrupted test case. isInterrupt :: AnException -> Bool
Test/QuickCheck/Function.hs view
@@ -1,4 +1,9 @@-{-# LANGUAGE TypeOperators, GADTs #-}+{-# LANGUAGE TypeOperators, GADTs, CPP #-}++#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ >= 708+{-# LANGUAGE PatternSynonyms #-}+#endif+ -- | Generation of random shrinkable, showable functions. -- See the paper \"Shrinking and showing functions\" by Koen Claessen. --@@ -24,6 +29,9 @@ , Function(..) , functionMap , functionShow+#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ >= 708+ , pattern Fn+#endif ) where @@ -37,11 +45,13 @@ import Data.Word import Data.List( intersperse ) import Data.Maybe( fromJust )+import Data.Ratio+import Control.Arrow( (&&&) ) -------------------------------------------------------------------------- -- concrete functions --- the type of possibly partial concrete functions+-- | The type of possibly partial concrete functions data a :-> c where Pair :: (a :-> (b :-> c)) -> ((a,b) :-> c) (:+:) :: (a :-> c) -> (b :-> c) -> (Either a b :-> c)@@ -186,6 +196,9 @@ ord' c = fromIntegral (ord c) :: Word8 chr' n = chr (fromIntegral n) +instance (Function a, Integral a) => Function (Ratio a) where+ function = functionMap (numerator &&& denominator) (uncurry (%))+ -- poly instances instance Function A where@@ -260,6 +273,15 @@ -- the Fun modifier data Fun a b = Fun (a :-> b, b) (a -> b)++#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ >= 708+-- | A pattern for matching against the function only:+--+-- > prop :: Fun String Integer -> Bool+-- > prop (Fn f) = f "banana" == f "monkey"+-- || f "banana" == f "elephant"+pattern Fn f <- Fun _ f+#endif mkFun :: (a :-> b) -> b -> Fun a b mkFun p d = Fun (p,d) (abstract p d)
Test/QuickCheck/Gen.hs view
@@ -19,12 +19,19 @@ import Control.Monad ( liftM , ap+ , replicateM+ , filterM ) import Control.Applicative ( Applicative(..)+ , (<$>) ) +import Control.Arrow+ ( second+ )+ import Test.QuickCheck.Random --------------------------------------------------------------------------@@ -69,8 +76,14 @@ -- | Overrides the size parameter. Returns a generator which uses -- the given size instead of the runtime-size parameter. resize :: Int -> Gen a -> Gen a-resize n (MkGen m) = MkGen (\r _ -> m r n)+resize n _ | n < 0 = error "Test.QuickCheck.resize: negative size"+resize n (MkGen g) = MkGen (\r _ -> g r n) +-- | Adjust the size parameter, by transforming it with the given+-- function.+scale :: (Int -> Int) -> Gen a -> Gen a+scale f g = sized (\n -> resize (f n) g)+ -- | Generates a random element in the given inclusive range. choose :: Random a => (a,a) -> Gen a choose rng = MkGen (\r _ -> let (x,_) = randomR rng r in x)@@ -135,6 +148,20 @@ elements [] = error "QuickCheck.elements used with empty list" elements xs = (xs !!) `fmap` choose (0, length xs - 1) +-- | Generates a random subsequence of the given list.+sublistOf :: [a] -> Gen [a]+sublistOf xs = filterM (\_ -> choose (False, True)) xs++-- | Generates a random permutation of the given list.+shuffle :: [a] -> Gen [a]+shuffle [] = return []+shuffle xs = do+ (y, ys) <- elements (selectOne xs)+ (y:) <$> shuffle ys+ where+ selectOne [] = []+ selectOne (y:ys) = (y,ys) : map (second (y:)) (selectOne ys)+ -- | Takes a list of elements of increasing size, and chooses -- among an initial segment of the list. The size of this initial -- segment increases with the size parameter.@@ -171,7 +198,7 @@ -- | Generates a list of the given length. vectorOf :: Int -> Gen a -> Gen [a]-vectorOf k gen = sequence [ gen | _ <- [1..k] ]+vectorOf = replicateM -- | Generates an infinite list. infiniteListOf :: Gen a -> Gen [a]
Test/QuickCheck/Gen/Unsafe.hs view
@@ -3,7 +3,7 @@ {-# LANGUAGE Rank2Types #-} #endif -- | Unsafe combinators for the 'Gen' monad.--- +-- -- 'Gen' is only morally a monad: two generators that are supposed -- to be equal will give the same probability distribution, but they -- might be different as functions from random number seeds to values.
Test/QuickCheck/Monadic.hs view
@@ -2,11 +2,70 @@ #ifndef NO_ST_MONAD {-# LANGUAGE Rank2Types #-} #endif--- | Allows testing of monadic values.--- See the paper \"Testing Monadic Code with QuickCheck\":--- <http://www.cse.chalmers.se/~rjmh/Papers/QuickCheckST.ps>.-module Test.QuickCheck.Monadic where+{-|+Module : Test.QuickCheck.Monadic +Allows testing of monadic values. Will generally follow this form:++@+prop_monadic a b = 'monadicIO' $ do+ a\' \<- 'run' (f a)+ b\' \<- 'run' (f b)+ -- ...+ 'assert' someBoolean+@++Example using the @FACTOR(1)@ command-line utility:++@+import System.Process+import Test.QuickCheck+import Test.QuickCheck.Monadic++-- $ factor 16+-- 16: 2 2 2 2+factor :: Integer -> IO [Integer]+factor n = parse \`fmap\` 'System.Process.readProcess' \"factor\" [show n] \"\" where++ parse :: String -> [Integer]+ parse = map read . tail . words++prop_factor :: Positive Integer -> Property+prop_factor ('Test.QuickCheck.Modifiers.Positive' n) = 'monadicIO' $ do+ factors \<- 'run' (factor n)++ 'assert' (product factors == n)+@++>>> quickCheck prop_factor++++ OK, passed 100 tests.++See the paper \"<http://www.cse.chalmers.se/~rjmh/Papers/QuickCheckST.ps Testing Monadic Code with QuickCheck>\".+-}+module Test.QuickCheck.Monadic (+ -- * Property monad+ PropertyM(..)++ -- * Monadic specification combinators+ , run+ , assert+ , pre+ , wp+ , pick+ , forAllM+ , monitor+ , stop++ -- * Run functions+ , monadic+ , monadic'+ , monadicIO+#ifndef NO_ST_MONAD+ , monadicST+ , runSTGen+#endif+ ) where+ -------------------------------------------------------------------------- -- imports @@ -27,6 +86,12 @@ -------------------------------------------------------------------------- -- type PropertyM +-- | The property monad is really a monad transformer that can contain+-- monadic computations in the monad @m@ it is parameterized by:+--+-- * @m@ - the @m@-computations that may be performed within @PropertyM@+--+-- Elements of @PropertyM m a@ may mix property operations and @m@-computations. newtype PropertyM m a = MkPropertyM { unPropertyM :: (a -> Gen (m Property)) -> Gen (m Property) } @@ -54,20 +119,49 @@ stop p = MkPropertyM (\_k -> return (return (property p))) -- should think about strictness/exceptions here---assert :: Testable prop => prop -> PropertyM m ()+-- assert :: Testable prop => prop -> PropertyM m ()+-- | Allows embedding non-monadic properties into monadic ones. assert :: Monad m => Bool -> PropertyM m ()-assert True = return ()+assert True = return () assert False = fail "Assertion failed" -- should think about strictness/exceptions here+-- | Tests preconditions. Unlike 'assert' this does not cause the+-- property to fail, rather it discards them just like using the+-- implication combinator 'Test.QuickCheck.Property.==>'.+--+-- This allows representing the <https://en.wikipedia.org/wiki/Hoare_logic Hoare triple>+--+-- > {p} x ← e{q}+--+-- as+--+-- @+-- pre p+-- x \<- run e+-- assert q+-- @+-- pre :: Monad m => Bool -> PropertyM m ()-pre True = return ()+pre True = return () pre False = stop rejected -- should be called lift?+-- | The lifting operation of the property monad. Allows embedding+-- monadic\/'IO'-actions in properties:+--+-- @+-- log :: Int -> IO ()+--+-- prop_foo n = monadicIO $ do+-- run (log n)+-- -- ...+-- @ run :: Monad m => m a -> PropertyM m a run m = MkPropertyM (liftM (m >>=) . promote) +-- | Quantification in a monadic property, fits better with+-- /do-notation/ than 'forAllM'. pick :: (Monad m, Show a) => Gen a -> PropertyM m a pick gen = MkPropertyM $ \k -> do a <- gen@@ -75,12 +169,33 @@ return (do p <- mp return (forAll (return a) (const p))) +-- | The <https://en.wikipedia.org/wiki/Predicate_transformer_semantics#Weakest_preconditions weakest precondition>+--+-- > wp(x ← e, p)+--+-- can be expressed as in code as @wp e (\\x -> p)@. wp :: Monad m => m a -> (a -> PropertyM m b) -> PropertyM m b wp m k = run m >>= k +-- | An alternative to quantification a monadic properties to 'pick',+-- with a notation similar to 'forAll'.+ forAllM :: (Monad m, Show a) => Gen a -> (a -> PropertyM m b) -> PropertyM m b forAllM gen k = pick gen >>= k +-- | Allows making observations about the test data:+--+-- @+-- monitor ('collect' e)+-- @+--+-- collects the distribution of value of @e@.+--+-- @+-- monitor ('counterexample' "Failure!")+-- @+--+-- Adds @"Failure!"@ to the counterexamples. monitor :: Monad m => (Property -> Property) -> PropertyM m () monitor f = MkPropertyM (\k -> (f `liftM`) `fmap` (k ())) @@ -92,10 +207,39 @@ monadic' :: Monad m => PropertyM m a -> Gen (m Property) monadic' (MkPropertyM m) = m (const (return (return (property True)))) +-- | Runs the property monad for 'IO'-computations.+--+-- @+-- prop_cat msg = monadicIO $ do+-- (exitCode, stdout, _) \<- run ('System.Process.readProcessWithExitCode' "cat" [] msg)+--+-- pre ('System.Exit.ExitSuccess' == exitCode)+--+-- assert (stdout == msg)+-- @+--+-- >>> quickCheck prop_cat+-- +++ OK, passed 100 tests.+-- monadicIO :: PropertyM IO a -> Property monadicIO = monadic ioProperty #ifndef NO_ST_MONAD+-- | Runs the property monad for 'ST'-computations.+--+-- @+-- -- Your mutable sorting algorithm here+-- sortST :: Ord a => [a] -> 'Control.Monad.ST.ST' s (MVector s a)+-- sortST = 'Data.Vector.thaw' . 'Data.Vector.fromList' . 'Data.List.sort'+--+-- prop_sortST xs = monadicST $ do+-- sorted \<- run ('Data.Vector.freeze' =<< sortST xs)+-- assert ('Data.Vector.toList' sorted == sort xs)+-- @+--+-- >>> quickCheck prop_sortST+-- +++ OK, passed 100 tests.+-- monadicST :: (forall s. PropertyM (ST s) a) -> Property monadicST m = property (runSTGen (monadic' m))
Test/QuickCheck/Property.hs view
@@ -13,7 +13,7 @@ import Test.QuickCheck.Arbitrary import Test.QuickCheck.Text( showErr, isOneLine, putLine ) import Test.QuickCheck.Exception-import Test.QuickCheck.State+import Test.QuickCheck.State hiding (labels) #ifndef NO_TIMEOUT import System.Timeout(timeout)@@ -21,6 +21,10 @@ import Data.Maybe import Control.Applicative import Control.Monad+import qualified Data.Map as Map+import Data.Map(Map)+import qualified Data.Set as Set+import Data.Set(Set) -------------------------------------------------------------------------- -- fixities@@ -168,23 +172,23 @@ -- k must be total m >>= k = joinRose (fmap k m) --- Execute the "IORose" bits of a rose tree, returning a tree+-- | Execute the "IORose" bits of a rose tree, returning a tree -- constructed by MkRose. reduceRose :: Rose Result -> IO (Rose Result) reduceRose r@(MkRose _ _) = return r reduceRose (IORose m) = m >>= reduceRose --- Apply a function to the outermost MkRose constructor of a rose tree.+-- | Apply a function to the outermost MkRose constructor of a rose tree. -- The function must be total! onRose :: (a -> [Rose a] -> Rose a) -> Rose a -> Rose a onRose f (MkRose x rs) = f x rs onRose f (IORose m) = IORose (fmap (onRose f) m) --- Wrap a rose tree in an exception handler.+-- | Wrap a rose tree in an exception handler. protectRose :: IO (Rose Result) -> IO (Rose Result) protectRose = protect (return . exception "Exception") --- Wrap all the Results in a rose tree in exception handlers.+-- | Wrap all the Results in a rose tree in exception handlers. protectResults :: Rose Result -> Rose Result protectResults = onRose $ \x rs -> IORose $ do@@ -208,7 +212,8 @@ , reason :: String -- ^ a message indicating what went wrong , theException :: Maybe AnException -- ^ the exception thrown, if any , abort :: Bool -- ^ if True, the test should not be repeated- , stamp :: [(String,Int)] -- ^ the collected values for this test case+ , labels :: Map String Int -- ^ all labels used by this property+ , stamp :: Set String -- ^ the collected values for this test case , callbacks :: [Callback] -- ^ the callbacks for this test case } @@ -220,7 +225,8 @@ , reason = "" , theException = Nothing , abort = False- , stamp = []+ , labels = Map.empty+ , stamp = Set.empty , callbacks = [] } @@ -364,17 +370,22 @@ -> prop -> Property classify b s = cover b 0 s --- | Checks that at least the given proportion of the test cases belong--- to the given class.+-- | Checks that at least the given proportion of /successful/ test+-- cases belong to the given class. Discarded tests (i.e. ones+-- with a false precondition) do not affect coverage. cover :: Testable prop => Bool -- ^ @True@ if the test case belongs to the class. -> Int -- ^ The required percentage (0-100) of test cases. -> String -- ^ Label for the test case class. -> prop -> Property-cover True n s = n `seq` s `listSeq` (mapTotalResult $ \res -> res { stamp = (s,n) : stamp res })+cover x n s =+ x `seq` n `seq` s `listSeq`+ mapTotalResult $+ \res -> res {+ labels = Map.insertWith max s n (labels res),+ stamp = if x then Set.insert s (stamp res) else stamp res } where [] `listSeq` z = z (x:xs) `listSeq` z = x `seq` xs `listSeq` z-cover False _ _ = property -- | Implication for properties: The resulting property holds if -- the first argument is 'False' (in which case the test case is discarded),@@ -440,20 +451,20 @@ conjoin ps = MkProperty $ do roses <- mapM (fmap unProp . unProperty . property) ps- return (MkProp (conj [] roses))+ return (MkProp (conj id roses)) where- conj cbs [] =- MkRose succeeded{callbacks = cbs} []+ conj k [] =+ MkRose (k succeeded) [] - conj cbs (p : ps) = IORose $ do+ conj k (p : ps) = IORose $ do rose@(MkRose result _) <- reduceRose p case ok result of _ | not (expect result) -> return (return failed { reason = "expectFailure may not occur inside a conjunction" })- Just True -> return (conj (cbs ++ callbacks result) ps)+ Just True -> return (conj (addLabels result . addCallbacks result . k) ps) Just False -> return rose Nothing -> do- rose2@(MkRose result2 _) <- reduceRose (conj (cbs ++ callbacks result) ps)+ rose2@(MkRose result2 _) <- reduceRose (conj (addCallbacks result . k) ps) return $ -- Nasty work to make sure we use the right callbacks case ok result2 of@@ -461,6 +472,12 @@ Just False -> rose2 Nothing -> rose2 + addCallbacks result r =+ r { callbacks = callbacks result ++ callbacks r }+ addLabels result r =+ r { labels = Map.unionWith max (labels result) (labels r),+ stamp = Set.union (stamp result) (stamp r) }+ -- | Disjunction: 'p1' '.||.' 'p2' passes unless 'p1' and 'p2' simultaneously fail. (.||.) :: (Testable prop1, Testable prop2) => prop1 -> prop2 -> Property p1 .||. p2 = disjoin [property p1, property p2]@@ -481,12 +498,25 @@ Just False -> do result2 <- q return $- if expect result2 then- case ok result2 of- Just True -> result2- Just False -> result1 >>> result2- Nothing -> result2- else expectFailureError+ case ok result2 of+ _ | not (expect result2) -> expectFailureError+ Just True -> result2+ Just False ->+ MkResult {+ ok = Just False,+ expect = True,+ reason = sep (reason result1) (reason result2),+ theException = theException result1 `mplus` theException result2,+ -- The following three fields are not important because the+ -- test case has failed anyway+ abort = False,+ labels = Map.empty,+ stamp = Set.empty,+ callbacks =+ callbacks result1 +++ [PostFinalFailure Counterexample $ \st _res -> putLine (terminal st) ""] +++ callbacks result2 }+ Nothing -> result2 Nothing -> do result2 <- q return (case ok result2 of@@ -495,16 +525,9 @@ _ -> result1) expectFailureError = failed { reason = "expectFailure may not occur inside a disjunction" }- result1 >>> result2 | not (expect result1 && expect result2) = expectFailureError- result1 >>> result2 =- result2- { reason = if null (reason result2) then reason result1 else reason result2- , theException = if null (reason result2) then theException result1 else theException result2- , stamp = stamp result1 ++ stamp result2- , callbacks = callbacks result1 ++- [PostFinalFailure Counterexample $ \st _res -> putLine (terminal st) ""] ++- callbacks result2- }+ sep [] s = s+ sep s [] = s+ sep s s' = s ++ ", " ++ s' -- | Like '==', but prints a counterexample when it fails. infix 4 ===
Test/QuickCheck/State.hs view
@@ -3,6 +3,9 @@ import Test.QuickCheck.Text import Test.QuickCheck.Random+import qualified Data.Map as Map+import Data.Map(Map)+import Data.Set(Set) -------------------------------------------------------------------------- -- State@@ -16,20 +19,21 @@ , maxSuccessTests :: Int -- ^ maximum number of successful tests needed , maxDiscardedTests :: Int -- ^ maximum number of tests that can be discarded , computeSize :: Int -> Int -> Int -- ^ how to compute the size of test cases from- -- #tests and #discarded tests+ -- #tests and #discarded tests - -- dynamic- , numSuccessTests :: Int -- ^ the current number of tests that have succeeded- , numDiscardedTests :: Int -- ^ the current number of discarded tests- , numRecentlyDiscardedTests :: Int -- ^ the number of discarded tests since the last successful test- , collected :: [[(String,Int)]] -- ^ all labels that have been collected so far- , expectedFailure :: Bool -- ^ indicates if the property is expected to fail- , randomSeed :: QCGen -- ^ the current random seed+ -- dynamic+ , numSuccessTests :: !Int -- ^ the current number of tests that have succeeded+ , numDiscardedTests :: !Int -- ^ the current number of discarded tests+ , numRecentlyDiscardedTests :: !Int -- ^ the number of discarded tests since the last successful test+ , labels :: !(Map String Int) -- ^ all labels that have been defined so far+ , collected :: ![Set String] -- ^ all labels that have been collected so far+ , expectedFailure :: !Bool -- ^ indicates if the property is expected to fail+ , randomSeed :: !QCGen -- ^ the current random seed -- shrinking- , numSuccessShrinks :: Int -- ^ number of successful shrinking steps so far- , numTryShrinks :: Int -- ^ number of failed shrinking steps since the last successful shrink- , numTotTryShrinks :: Int -- ^ total number of failed shrinking steps+ , numSuccessShrinks :: !Int -- ^ number of successful shrinking steps so far+ , numTryShrinks :: !Int -- ^ number of failed shrinking steps since the last successful shrink+ , numTotTryShrinks :: !Int -- ^ total number of failed shrinking steps } --------------------------------------------------------------------------
Test/QuickCheck/Test.hs view
@@ -9,13 +9,16 @@ -- imports import Test.QuickCheck.Gen-import Test.QuickCheck.Property hiding ( Result( reason, theException) )+import Test.QuickCheck.Property hiding ( Result( reason, theException, labels ) ) import qualified Test.QuickCheck.Property as P import Test.QuickCheck.Text-import Test.QuickCheck.State+import Test.QuickCheck.State hiding (labels)+import qualified Test.QuickCheck.State as S import Test.QuickCheck.Exception import Test.QuickCheck.Random import System.Random(split)+import qualified Data.Map as Map+import qualified Data.Set as Set import Data.Char ( isSpace@@ -76,6 +79,12 @@ , labels :: [(String,Int)] -- Labels and frequencies found during all successful tests , output :: String -- Printed output }+ -- | The tests passed but a use of 'cover' had insufficient coverage+ | InsufficientCoverage+ { numTests :: Int -- Number of tests performed+ , labels :: [(String,Int)] -- Labels and frequencies found during all successful tests+ , output :: String -- Printed output+ } deriving ( Show ) -- | Check if the test run result was a success@@ -121,6 +130,7 @@ , numSuccessTests = 0 , numDiscardedTests = 0 , numRecentlyDiscardedTests = 0+ , S.labels = Map.empty , collected = [] , expectedFailure = False , randomSeed = rnd@@ -173,31 +183,34 @@ | otherwise = runATest st f doneTesting :: State -> (QCGen -> Int -> Prop) -> IO 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)- if expectedFailure st then- return Success{ labels = summary st,- numTests = numSuccessTests st,- output = theOutput }- else- return NoExpectedFailure{ labels = summary st,- numTests = numSuccessTests st,- output = theOutput }+doneTesting st _f+ | not (expectedFailure st) = do+ putPart (terminal st)+ ( bold ("*** Failed!")+ ++ " Passed "+ ++ show (numSuccessTests st)+ ++ " tests (expected failure)"+ )+ finished NoExpectedFailure+ | insufficientCoverage st = do+ putPart (terminal st)+ ( bold ("*** Insufficient coverage after ")+ ++ show (numSuccessTests st)+ ++ " tests"+ )+ finished InsufficientCoverage+ | otherwise = do+ putPart (terminal st)+ ( "+++ OK, passed "+ ++ show (numSuccessTests st)+ ++ " tests"+ )+ finished Success+ where+ finished k = do+ success st+ theOutput <- terminalOutput (terminal st)+ return (k (numSuccessTests st) (summary st) theOutput) giveUp :: State -> (QCGen -> Int -> Prop) -> IO Result giveUp st _f =@@ -228,10 +241,13 @@ ) let size = computeSize st (numSuccessTests st) (numRecentlyDiscardedTests st) MkRose res ts <- protectRose (reduceRose (unProp (f rnd1 size)))- callbackPostTest st res+ res <- callbackPostTest st res let continue break st' | abort res = break st' | otherwise = test st'+ cons x xs+ | Set.null x = xs+ | otherwise = x:xs case res of MkResult{ok = Just True, stamp = stamp, expect = expect} -> -- successful test@@ -239,7 +255,8 @@ st{ numSuccessTests = numSuccessTests st + 1 , numRecentlyDiscardedTests = 0 , randomSeed = rnd2- , collected = stamp : collected st+ , S.labels = Map.unionWith max (S.labels st) (P.labels res)+ , collected = stamp `cons` collected st , expectedFailure = expect } f @@ -248,6 +265,7 @@ st{ numDiscardedTests = numDiscardedTests st + 1 , numRecentlyDiscardedTests = numRecentlyDiscardedTests st + 1 , randomSeed = rnd2+ , S.labels = Map.unionWith max (S.labels st) (P.labels res) , expectedFailure = expect } f @@ -282,10 +300,9 @@ . map (\ss -> (head ss, (length ss * 100) `div` numSuccessTests st)) . group . sort- $ [ concat (intersperse ", " s')+ $ [ concat (intersperse ", " (Set.toList s)) | s <- collected st- , let s' = [ t | (t,_) <- s ]- , not (null s')+ , not (Set.null s) ] success :: State -> IO ()@@ -307,30 +324,28 @@ . sort $ [ concat (intersperse ", " s') | s <- collected st- , let s' = [ t | (t,0) <- s ]+ , let s' = [ t | t <- Set.toList s, Map.lookup t (S.labels st) == Just 0 ] , 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+ covers = [ ("only " ++ show (labelPercentage l st) ++ "% " ++ l ++ ", not " ++ show reqP ++ "%")+ | (l, reqP) <- Map.toList (S.labels st)+ , labelPercentage l st < reqP ] - (x,_) `first` (y,_) = x == y+ showP p = (if p < 10 then " " else "") ++ show p ++ "% " - maxi = map (\lps -> (fst (head lps), maximum (map snd lps)))- . groupBy first- . sort+labelPercentage :: String -> State -> Int+labelPercentage l st =+ -- XXX in case of a disjunction, a label can occur several times,+ -- need to think what to do there+ (100 * occur) `div` maxSuccessTests st+ where+ occur = length [ l' | l' <- concat (map Set.toList (collected st)), l == l' ] - showP p = (if p < 10 then " " else "") ++ show p ++ "% "+insufficientCoverage :: State -> Bool+insufficientCoverage st =+ or [ labelPercentage l st < reqP | (l, reqP) <- Map.toList (S.labels st) ] -------------------------------------------------------------------------- -- main shrinking loop@@ -343,33 +358,38 @@ 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+ r <- tryEvaluateIO $+ 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+ ]+ ++ ")..."+ ) case r of Left err ->- localMinFound st- (exception "Exception while generating shrink-list" err) { callbacks = callbacks res }- Right ts' -> localMin' st res ts'+ localMinFound st (exception "Exception while printing status message" err) { callbacks = callbacks res }+ Right () -> do+ 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'+ res' <- callbackPostTest st res' if ok res' == Just False then localMin st{ numSuccessShrinks = numSuccessShrinks st + 1, numTryShrinks = 0 } res' res ts'@@ -394,28 +414,27 @@ | msg <- lines (P.reason res) ] callbackPostFinalFailure st res+ -- NB no need to check if callbacks threw an exception because+ -- we are about to return to the user anyway 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 ]+callbackPostTest :: State -> P.Result -> IO P.Result+callbackPostTest st res = protect (exception "Exception running callback") $ do+ sequence_ [ f st res | PostTest _ f <- callbacks res ]+ return 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+callbackPostFinalFailure st res = do+ x <- tryEvaluateIO $ sequence_ [ f st res | PostFinalFailure _ f <- callbacks res ]+ case x of+ Left err -> do+ putLine (terminal st) "*** Exception running callback: "+ tryEvaluateIO $ putLine (terminal st) (show err)+ return ()+ Right () -> return () -------------------------------------------------------------------------- -- the end.
Test/QuickCheck/Text.hs view
@@ -85,15 +85,13 @@ -- putting strings data Terminal- = MkTerminal (IORef (IO ())) Output Output--data Output- = Output (String -> IO ()) (IORef String)+ = MkTerminal (IORef String) (IORef Int) (String -> IO ()) (String -> IO ()) -newTerminal :: Output -> Output -> IO Terminal+newTerminal :: (String -> IO ()) -> (String -> IO ()) -> IO Terminal newTerminal out err =- do ref <- newIORef (return ())- return (MkTerminal ref out err)+ do res <- newIORef ""+ tmp <- newIORef 0+ return (MkTerminal res tmp out err) withBuffering :: IO a -> IO a withBuffering action = do@@ -104,65 +102,39 @@ action `finally` hSetBuffering stderr mode withStdioTerminal :: (Terminal -> IO a) -> IO a-withStdioTerminal action = do- out <- output (handle stdout)- err <- output (handle stderr)- withBuffering (newTerminal out err >>= action)+withStdioTerminal action =+ withBuffering (newTerminal (handle stdout) (handle stderr) >>= action) withNullTerminal :: (Terminal -> IO a) -> IO a-withNullTerminal action = do- out <- output (const (return ()))- err <- output (const (return ()))- newTerminal out err >>= action+withNullTerminal action =+ newTerminal (const (return ())) (const (return ())) >>= action terminalOutput :: Terminal -> IO String-terminalOutput (MkTerminal _ out _) = get out+terminalOutput (MkTerminal res _ _ _) = readIORef res handle :: Handle -> String -> IO () handle h s = do hPutStr h s hFlush h -output :: (String -> IO ()) -> IO Output-output f = do- r <- newIORef ""- return (Output f r)--put :: Output -> String -> IO ()-put (Output f r) s = do- f s- modifyIORef r (++ s)--get :: Output -> IO String-get (Output _ r) = readIORef r- flush :: Terminal -> IO ()-flush (MkTerminal ref _ _) =- do io <- readIORef ref- writeIORef ref (return ())- io--postpone :: Terminal -> IO () -> IO ()-postpone (MkTerminal ref _ _) io' =- do io <- readIORef ref- writeIORef ref (io >> io')+flush (MkTerminal _ tmp _ err) =+ do n <- readIORef tmp+ writeIORef tmp 0+ err (replicate n ' ' ++ replicate n '\b') putPart, putTemp, putLine :: Terminal -> String -> IO ()-putPart tm@(MkTerminal _ out _) s =+putPart tm@(MkTerminal res _ out _) s = do flush tm- put out s+ out s+ modifyIORef res (++ s) -putTemp tm@(MkTerminal _ _ err) s =- do flush tm- put err (s ++ [ '\b' | _ <- s ])- postpone tm $- put err ( [ ' ' | _ <- s ]- ++ [ '\b' | _ <- s ]- )+putLine tm s = putPart tm (s ++ "\n") -putLine tm@(MkTerminal _ out _) s =+putTemp tm@(MkTerminal _ tmp _ err) s = do flush tm- put out (s ++ "\n")+ err (s ++ [ '\b' | _ <- s ])+ modifyIORef tmp (+ length s) -------------------------------------------------------------------------- -- the end.
changelog view
@@ -1,3 +1,24 @@+QuickCheck 2.8 (released 2015-03-18)+ * New features:+ * Support for GHC 7.10+ * Arbitrary instance for Natural+ * New generators shuffle and sublistOf+ * Support for generic coarbitrary+ * When using the cover combinator, insufficient coverage now+ causes the property to fail++ * API changes:+ * Test.QuickCheck.Function: new pattern synonym Fn+ * genericShrink no longer requires Typeable+ * Result has a new constructor InsufficientCoverage+ * resize throws an error if the size is negative++ * Bug fixes:+ * Fix memory leaks+ * Exceptions thrown by callbacks now cause the test to fail+ * Fixed a bug where the cover combinator wouldn't give a+ warning if coverage was 0%+ QuickCheck 2.7.3 (released 2014-03-24) * Add annotations for Safe Haskell.
examples/Heap.hs view
@@ -25,7 +25,7 @@ = Node a (Heap a) (Heap a) | Empty deriving ( Eq, Ord, Show )- + empty :: Heap a empty = Empty @@ -53,13 +53,13 @@ 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) + merging hs = merging (sweep hs) sweep [] = [] sweep [h] = [h]@@ -124,7 +124,7 @@ h ==? xs && xs == sort xs where xs = toSortedList h- + -------------------------------------------------------------------------- -- generators @@ -141,7 +141,7 @@ where arbHeap2 = arbHeap (Just y) (n `div` 2)) | n > 0 ]- + -------------------------------------------------------------------------- -- main