QuickCheck 2.7.6 → 2.18.0.0
raw patch · 61 files changed
Files
- LICENSE +4/−3
- QuickCheck.cabal +217/−77
- README +6/−9
- Test/QuickCheck.hs +0/−156
- Test/QuickCheck/All.hs +0/−179
- Test/QuickCheck/Arbitrary.hs +0/−715
- Test/QuickCheck/Exception.hs +0/−128
- Test/QuickCheck/Function.hs +0/−283
- Test/QuickCheck/Gen.hs +0/−181
- Test/QuickCheck/Gen/Unsafe.hs +0/−51
- Test/QuickCheck/Modifiers.hs +0/−346
- Test/QuickCheck/Monadic.hs +0/−109
- Test/QuickCheck/Poly.hs +0/−133
- Test/QuickCheck/Property.hs +0/−517
- Test/QuickCheck/Random.hs +0/−108
- Test/QuickCheck/State.hs +0/−36
- Test/QuickCheck/Test.hs +0/−421
- Test/QuickCheck/Text.hs +0/−168
- changelog +0/−73
- changelog.md +457/−0
- examples/Heap.hs +18/−23
- examples/Heap_Program.hs +197/−0
- examples/Heap_ProgramAlgebraic.hs +254/−0
- examples/Lambda.hs +363/−0
- examples/Merge.hs +116/−0
- examples/Set.hs +207/−0
- examples/Simple.hs +46/−0
- make-hugs +28/−0
- src/Test/QuickCheck.hs +381/−0
- src/Test/QuickCheck/All.hs +229/−0
- src/Test/QuickCheck/Arbitrary.hs +1981/−0
- src/Test/QuickCheck/Compat.hs +34/−0
- src/Test/QuickCheck/Exception.hs +118/−0
- src/Test/QuickCheck/Features.hs +108/−0
- src/Test/QuickCheck/Function.hs +772/−0
- src/Test/QuickCheck/Gen.hs +385/−0
- src/Test/QuickCheck/Gen/Unsafe.hs +53/−0
- src/Test/QuickCheck/Modifiers.hs +549/−0
- src/Test/QuickCheck/Monadic.hs +368/−0
- src/Test/QuickCheck/Monoids.hs +83/−0
- src/Test/QuickCheck/Poly.hs +179/−0
- src/Test/QuickCheck/Property.hs +1099/−0
- src/Test/QuickCheck/Random.hs +131/−0
- src/Test/QuickCheck/State.hs +111/−0
- src/Test/QuickCheck/Test.hs +780/−0
- src/Test/QuickCheck/Text.hs +236/−0
- test-hugs +29/−0
- test-mhs +25/−0
- tests/CollectDataTypes.hs +199/−0
- tests/DiscardRatio.hs +56/−0
- tests/GCoArbitraryExample.hs +24/−0
- tests/GShrinkExample.hs +20/−0
- tests/Generators.hs +220/−0
- tests/Misc.hs +50/−0
- tests/MonadFix.hs +26/−0
- tests/Monoids.hs +146/−0
- tests/RunCollectDataTypes.hs +89/−0
- tests/Split.hs +28/−0
- tests/Strictness.hs +42/−0
- tests/Terminal.hs +94/−0
- tests/WithProgress.hs +68/−0
LICENSE view
@@ -1,7 +1,8 @@-Copyright (c) 2000-2014, Koen Claessen+(The following is the 3-clause BSD license.)++Copyright (c) 2000-2019, Koen Claessen Copyright (c) 2006-2008, Björn Bringert-Copyright (c) 2009-2014, Nick Smallbone-All rights reserved.+Copyright (c) 2009-2019, Nick Smallbone Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
QuickCheck.cabal view
@@ -1,32 +1,56 @@ Name: QuickCheck-Version: 2.7.6-Cabal-Version: >= 1.8+Version: 2.18.0.0+Cabal-Version: >= 1.10 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-2019 Koen Claessen, 2006-2008 Björn Bringert, 2009-2019 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+Maintainer: Nick Smallbone <nick@smallbone.se>+Bug-reports: https://github.com/nick8325/quickcheck/issues+Tested-with: GHC >= 8.10 && < 9.15, hugs Homepage: https://github.com/nick8325/quickcheck Category: Testing Synopsis: Automatic testing of Haskell programs Description: QuickCheck is a library for random testing of program properties.+ The programmer provides a specification of the program, in the form of+ properties which functions should satisfy, and QuickCheck then tests that the+ properties hold in a large number of randomly generated cases.+ Specifications are expressed in Haskell, using combinators provided by+ QuickCheck. QuickCheck provides combinators to define properties, observe the+ distribution of test data, and define test data generators. .- The programmer provides a specification of the program, in- the form of properties which functions should satisfy, and- QuickCheck then tests that the properties hold in a large number- of randomly generated cases.+ Most of QuickCheck's functionality is exported by the main "Test.QuickCheck"+ module. The main exception is the monadic property testing library in+ "Test.QuickCheck.Monadic". .- Specifications are expressed in- Haskell, using combinators defined in the QuickCheck library.- QuickCheck provides combinators to define properties, observe- the distribution of test data, and define test- data generators.+ If you are new to QuickCheck, you can try looking at the following resources:+ .+ * The <http://www.cse.chalmers.se/~rjmh/QuickCheck/manual.html official QuickCheck manual>.+ It's a bit out-of-date in some details and doesn't cover newer QuickCheck features,+ but is still full of good advice.+ * <https://begriffs.com/posts/2017-01-14-design-use-quickcheck.html>,+ a detailed tutorial written by a user of QuickCheck.+ .+ The <https://hackage.haskell.org/package/quickcheck-instances quickcheck-instances>+ companion package provides instances for types in Haskell Platform packages+ at the cost of additional dependencies. +extra-source-files:+ README+ changelog.md+ examples/Heap.hs+ examples/Heap_Program.hs+ examples/Heap_ProgramAlgebraic.hs+ examples/Lambda.hs+ examples/Merge.hs+ examples/Set.hs+ examples/Simple.hs+ make-hugs+ test-hugs+ test-mhs+ source-repository head type: git location: https://github.com/nick8325/quickcheck@@ -34,34 +58,51 @@ source-repository this type: git location: https://github.com/nick8325/quickcheck- tag: 2.7.6--flag base3- Description: Choose the new smaller, split-up base package.--flag base4- Description: Choose the even newer base package with extensible exceptions.+ tag: 2.18 flag templateHaskell Description: Build Test.QuickCheck.All, which uses Template Haskell.+ Default: True+ Manual: True +flag old-random+ Description: Build against a pre-1.2.0 version of the random package.+ Default: False+ Manual: False+ library- -- Choose which library versions to use.- if flag(base4)- Build-depends: base >= 4 && < 5, random+ Hs-source-dirs: src+ Build-depends: base >=4.14 && <5, containers+ Default-language: Haskell2010++ -- New vs old random.+ if flag(old-random)+ Build-depends: random >= 1.0.0.3 && < 1.2.0+ cpp-options: -DOLD_RANDOM else- if flag(base3)- Build-depends: base >= 3 && < 4, random- else- Build-depends: base < 3+ Build-depends: random >= 1.2.0 && < 1.4 + -- We always use splitmix directly rather than going through StdGen+ -- (it's somewhat more efficient).+ -- However, Hugs traps overflow on Word64, so we have to stick+ -- with StdGen there.+ if impl(hugs)+ cpp-options: -DNO_SPLITMIX+ else+ Build-depends: splitmix >= 0.1.0.2 && <0.2++ if impl(hugs)+ cpp-options: -DNO_SEMIGROUP -DNO_EXISTENTIAL_FIELD_SELECTORS+ -- Modules that are always built. Exposed-Modules: Test.QuickCheck,+ Test.QuickCheck.Compat, Test.QuickCheck.Arbitrary, Test.QuickCheck.Gen, Test.QuickCheck.Gen.Unsafe, Test.QuickCheck.Monadic,+ Test.QuickCheck.Monoids, Test.QuickCheck.Modifiers, Test.QuickCheck.Property, Test.QuickCheck.Test,@@ -69,78 +110,177 @@ Test.QuickCheck.Poly, Test.QuickCheck.State, Test.QuickCheck.Random,- Test.QuickCheck.Exception+ Test.QuickCheck.Exception,+ Test.QuickCheck.Features -- GHC-specific modules.- if impl(ghc)+ if impl(ghc) || impl(mhs) 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: transformers >= 0.3, deepseq >= 1.1.0.0++ if impl(ghc) && flag(templateHaskell) Build-depends: template-haskell >= 2.4+ Other-Extensions: TemplateHaskellQuotes Exposed-Modules: Test.QuickCheck.All else cpp-options: -DNO_TEMPLATE_HASKELL - -- Compiler-specific tweaks, lots of 'em!-- -- On old versions of GHC use the ghc package to catch ctrl-C.- if impl(ghc >= 6.7) && impl(ghc < 6.13)- Build-depends: ghc-- -- We want to use extensible-exceptions even if linking against base-3.- if impl(ghc >= 6.9) && impl (ghc < 7.0)- Build-depends: extensible-exceptions-- -- GHC < 7.0 can't cope with multiple LANGUAGE pragmas in the same file.- if impl(ghc < 7)- Extensions: GeneralizedNewtypeDeriving, MultiParamTypeClasses, Rank2Types, TypeOperators+ if !impl(ghc) && !impl(mhs)+ cpp-options: -DNO_CALLSTACK+ -DNO_SEMIGROUP+ -DNO_CTYPES_CONSTRUCTORS+ -DNO_FOREIGN_C_USECONDS+ -DNO_POLYKINDS+ -DNO_MONADFAIL+ -DNO_TRANSFORMERS+ -DNO_DEEPSEQ - -- The new generics appeared in GHC 7.2...- if impl(ghc < 7.2)- cpp-options: -DNO_GENERICS- -- ...but in 7.2-7.4 it lives in the ghc-prim package.- if impl(ghc >= 7.2) && impl(ghc < 7.6)- Build-depends: ghc-prim+ -- random is explicitly Trustworthy since 1.0.1.0+ -- similar constraint for containers+ if impl(ghc)+ Build-depends: random >=1.0.1.0+ , containers >=0.4.2.1 - -- Safe Haskell appeared in GHC 7.2, but GHC.Generics isn't safe until 7.4.- if impl (ghc < 7.4)- cpp-options: -DNO_SAFE_HASKELL+ if impl(ghc >= 9.8)+ ghc-options: -Wno-x-partial - -- Use tf-random on newer GHCs.- if impl(ghc >= 7) && flag(base4)- Build-depends: tf-random >= 0.4- else- cpp-options: -DNO_TF_RANDOM+ if impl(ghc < 9.4)+ Build-depends: data-array-byte -- Switch off most optional features on non-GHC systems.- if !impl(ghc)+ if !impl(ghc) && !impl(mhs) -- If your Haskell compiler can cope without some of these, please -- send a message to the QuickCheck mailing list!- cpp-options: -DNO_TIMEOUT -DNO_NEWTYPE_DERIVING -DNO_GENERICS -DNO_TEMPLATE_HASKELL -DNO_SAFE_HASKELL+ cpp-options: -DNO_TIMEOUT -DNO_NEWTYPE_DERIVING -DNO_GENERICS+ -DNO_TEMPLATE_HASKELL -DNO_SAFE_HASKELL -DNO_TYPEABLE -DNO_GADTS+ -DNO_EXTRA_METHODS_IN_APPLICATIVE -DOLD_RANDOM -DNO_CALLSTACK if !impl(hugs) && !impl(uhc) cpp-options: -DNO_ST_MONAD -DNO_MULTI_PARAM_TYPE_CLASSES + if impl(mhs)+ cpp-options: -DNO_GENERICS -DNO_TEMPLATE_HASKELL -DNO_CTYPES_CONSTRUCTORS+ -DNO_EXISTENTIAL_FIELD_SELECTORS+ -- LANGUAGE pragmas don't have any effect in Hugs. if impl(hugs)- Extensions: CPP-- if impl(uhc)- -- Cabal under UHC needs pointing out all the dependencies of the- -- random package.- Build-depends: old-time, old-locale- -- Plus some bits of the standard library are missing.- cpp-options: -DNO_FIXED -DNO_EXCEPTIONS+ Default-Extensions: CPP Test-Suite test-quickcheck type: exitcode-stdio-1.0+ Default-language: Haskell2010 hs-source-dirs: examples main-is: Heap.hs+ build-depends: base, QuickCheck+ if !flag(templateHaskell)+ Buildable: False++Test-Suite test-quickcheck-gcoarbitrary+ type: exitcode-stdio-1.0+ Default-language: Haskell2010+ hs-source-dirs: tests+ main-is: GCoArbitraryExample.hs+ build-depends: base, QuickCheck+ if !flag(templateHaskell)+ buildable: False++Test-Suite test-quickcheck-generators+ type: exitcode-stdio-1.0+ Default-language: Haskell2010+ hs-source-dirs: tests+ main-is: Generators.hs+ build-depends: base, QuickCheck+ if !flag(templateHaskell)+ Buildable: False++Test-Suite test-quickcheck-gshrink+ type: exitcode-stdio-1.0+ Default-language: Haskell2010+ hs-source-dirs: tests+ main-is: GShrinkExample.hs+ build-depends: base, QuickCheck+ if !flag(templateHaskell)+ buildable: False++Test-Suite test-quickcheck-terminal+ type: exitcode-stdio-1.0+ Default-language: Haskell2010+ hs-source-dirs: tests+ main-is: Terminal.hs+ build-depends: base, process, deepseq >= 1.1.0.0, QuickCheck+ if !flag(templateHaskell)+ buildable: False++Test-Suite test-quickcheck-monadfix+ type: exitcode-stdio-1.0+ Default-language: Haskell2010+ hs-source-dirs: tests+ main-is: MonadFix.hs+ build-depends: base, QuickCheck+ if !flag(templateHaskell)+ buildable: False++Test-Suite test-quickcheck-split+ type: exitcode-stdio-1.0+ Default-language: Haskell2010+ hs-source-dirs: tests+ main-is: Split.hs+ build-depends: base, QuickCheck+ if impl(ghc >= 9.8)+ ghc-options: -Wno-x-partial++Test-Suite test-quickcheck-strictness+ type: exitcode-stdio-1.0+ Default-language: Haskell2010+ hs-source-dirs: tests+ main-is: Strictness.hs+ build-depends: base, QuickCheck, containers+ if !flag(templateHaskell)+ buildable: False++Test-Suite test-quickcheck-misc+ type: exitcode-stdio-1.0+ Default-language: Haskell2010+ hs-source-dirs: tests+ main-is: Misc.hs+ build-depends: base, QuickCheck, containers+ if !flag(templateHaskell)+ buildable: False++Test-Suite test-quickcheck-discard+ type: exitcode-stdio-1.0+ Default-language: Haskell2010+ hs-source-dirs: tests+ main-is: DiscardRatio.hs+ build-depends: base, QuickCheck++Test-Suite test-quickcheck-monoids+ type: exitcode-stdio-1.0+ Default-language: Haskell2010+ hs-source-dirs: tests+ main-is: Monoids.hs+ build-depends: base, QuickCheck++Test-Suite test-quickcheck-withprogress+ type: exitcode-stdio-1.0+ Default-language: Haskell2010+ hs-source-dirs: tests+ main-is: WithProgress.hs+ build-depends: base, QuickCheck++Test-Suite test-quickcheck-instances+ type: exitcode-stdio-1.0+ Default-language: Haskell2010+ hs-source-dirs: tests+ other-modules: CollectDataTypes+ main-is: RunCollectDataTypes.hs build-depends: base,- QuickCheck == 2.7.6,- template-haskell >= 2.4,- test-framework >= 0.4 && < 0.9+ QuickCheck,+ transformers,+ split,+ process,+ template-haskell,+ hint+ if !flag(templateHaskell) || impl(ghc <= 8.10)+ buildable: False
README view
@@ -1,14 +1,11 @@ This is QuickCheck 2, a library for random testing of program properties. -Install it in the usual way:--$ cabal install--Please report bugs to the QuickCheck mailing list at-quickcheck@projects.haskell.org.+Add `QuickCheck` to your package dependencies to use it in tests or REPL. -If you get errors about Template Haskell, try+The quickcheck-instances [1] companion package provides instances for types in+Haskell Platform packages at the cost of additional dependencies. -$ cabal install -f-templateHaskell+The make-hugs script makes a Hugs-compatible version of QuickCheck.+It may also be useful for other non-GHC implementations. -but please report this as a bug.+[1]: http://hackage.haskell.org/package/quickcheck-instances
− Test/QuickCheck.hs
@@ -1,156 +0,0 @@-{-# LANGUAGE CPP #-}-#ifndef NO_SAFE_HASKELL-{-# LANGUAGE Safe #-}-#endif-module Test.QuickCheck- (- -- * Running tests- quickCheck- , Args(..), Result(..)- , stdArgs- , quickCheckWith- , quickCheckWithResult- , quickCheckResult- -- ** Running tests verbosely- , verboseCheck- , verboseCheckWith- , verboseCheckWithResult- , verboseCheckResult-#ifndef NO_TEMPLATE_HASKELL- -- ** Testing all properties in a module- , quickCheckAll- , verboseCheckAll- , forAllProperties- -- ** Testing polymorphic properties- , polyQuickCheck- , polyVerboseCheck- , monomorphic-#endif-- -- * Random generation- , Gen- -- ** Generator combinators- , choose- , oneof- , frequency- , elements- , growingElements- , sized- , resize- , suchThat- , suchThatMaybe- , listOf- , listOf1- , vectorOf- , infiniteListOf- -- ** Generators which use Arbitrary- , vector- , orderedList- , infiniteList- -- ** Running a generator- , generate- -- ** Generator debugging- , sample- , sample'-- -- * Arbitrary and CoArbitrary classes- , Arbitrary(..)- , CoArbitrary(..)-- -- ** Helper functions for implementing arbitrary- , arbitrarySizedIntegral- , arbitrarySizedFractional- , arbitrarySizedBoundedIntegral- , arbitraryBoundedIntegral- , arbitraryBoundedRandom- , arbitraryBoundedEnum- -- ** Helper functions for implementing shrink-#ifndef NO_GENERICS- , genericShrink- , subterms- , recursivelyShrink-#endif- , shrinkNothing- , shrinkList- , shrinkIntegral- , shrinkRealFrac- , shrinkRealFracToInteger- -- ** Helper functions for implementing coarbitrary- , variant- , coarbitraryIntegral- , coarbitraryReal- , coarbitraryShow- , coarbitraryEnum- , (><)-- -- ** Type-level modifiers for changing generator behavior- , Blind(..)- , Fixed(..)- , OrderedList(..)- , NonEmptyList(..)- , Positive(..)- , NonZero(..)- , NonNegative(..)- , Large(..)- , Small(..)- , Smart(..)- , Shrink2(..)-#ifndef NO_MULTI_PARAM_TYPE_CLASSES- , Shrinking(..)- , ShrinkState(..)-#endif-- -- * Properties- , Property, Testable(..)- -- ** Property combinators- , forAll- , forAllShrink- , shrinking- , (==>)- , (===)- , ioProperty- -- *** Controlling property execution- , verbose- , once- , within- , noShrinking- -- *** Conjunction and disjunction- , (.&.)- , (.&&.)- , conjoin- , (.||.)- , disjoin- -- *** What to do on failure- , counterexample- , printTestCase- , whenFail- , whenFail'- , expectFailure- -- *** Analysing test distribution- , label- , collect- , classify- , cover- -- *** Miscellaneous- , Discard(..)- , discard- , mapSize- )- where------------------------------------------------------------------------------- imports--import Test.QuickCheck.Gen-import Test.QuickCheck.Arbitrary-import Test.QuickCheck.Modifiers-import Test.QuickCheck.Property hiding ( Result(..) )-import Test.QuickCheck.Test-import Test.QuickCheck.Text-import Test.QuickCheck.Exception-#ifndef NO_TEMPLATE_HASKELL-import Test.QuickCheck.All-#endif------------------------------------------------------------------------------- the end.
− Test/QuickCheck/All.hs
@@ -1,179 +0,0 @@-{-# LANGUAGE TemplateHaskell, Rank2Types, CPP #-}-#ifndef NO_SAFE_HASKELL-{-# LANGUAGE Trustworthy #-}-#endif--- | Test all properties in the current module, using Template Haskell.--- You need to have a @{-\# LANGUAGE TemplateHaskell \#-}@ pragma in--- your module for any of these to work.-module Test.QuickCheck.All(- -- ** Testing all properties in a module- quickCheckAll,- verboseCheckAll,- forAllProperties,- -- ** Testing polymorphic properties- polyQuickCheck,- polyVerboseCheck,- monomorphic) where--import Language.Haskell.TH-import Test.QuickCheck.Property hiding (Result)-import Test.QuickCheck.Test-import Data.Char-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.--- Note that just evaluating @'quickCheck' prop@ in GHCi will seem to--- work, but will silently default all type variables to @()@!------ @$('polyQuickCheck' \'prop)@ means the same as--- @'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) |]--type Error = forall a. String -> a---- | Monomorphise an arbitrary property by defaulting all type variables to 'Integer'.------ 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)- let err msg = error $ msg ++ ": " ++ pprint ty0- (polys, ctx, ty) <- deconstructType err ty0- case polys of- [] -> return (VarE t)- _ -> do- integer <- [t| Integer |]- ty' <- monomorphiseType err integer ty- return (SigE (VarE t) ty')--infoType :: Info -> Type-infoType (ClassOpI _ ty _ _) = ty-infoType (DataConI _ ty _ _) = ty-infoType (VarI _ ty _ _) = ty--deconstructType :: Error -> Type -> Q ([Name], Cxt, Type)-deconstructType err ty0@(ForallT xs ctx ty) = do- let plain (PlainTV _) = True- 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)--monomorphiseType :: Error -> Type -> Type -> TypeQ-monomorphiseType err mono ty@(VarT n) = return mono-monomorphiseType err mono (AppT t1 t2) = liftM2 AppT (monomorphiseType err mono t1) (monomorphiseType err mono t2)-monomorphiseType err mono ty@(ForallT _ _ _) = err $ "Higher-ranked type"-monomorphiseType err mono ty = return ty---- | Test all properties in the current module, using a custom--- 'quickCheck' function. The same caveats as with 'quickCheckAll'--- apply.------ @$'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 (readUTF8File filename))- let prefixes = map (takeWhile (\c -> isAlphaNum c || 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 <- (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.------ 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 |]--runQuickCheckAll :: [(String, Property)] -> (Property -> IO Result) -> IO Bool-runQuickCheckAll ps qc =- fmap and . forM ps $ \(xs, p) -> do- putStrLn $ "=== " ++ xs ++ " ==="- r <- qc p- putStrLn ""- return $ case r of- Success { } -> True- Failure { } -> False- NoExpectedFailure { } -> False- GaveUp { } -> False
− Test/QuickCheck/Arbitrary.hs
@@ -1,715 +0,0 @@--- | Type classes for random generation of values.-{-# LANGUAGE CPP #-}-#ifndef NO_GENERICS-{-# LANGUAGE DefaultSignatures, FlexibleContexts, TypeOperators #-}-#endif-module Test.QuickCheck.Arbitrary- (- -- * Arbitrary and CoArbitrary classes- Arbitrary(..)- , CoArbitrary(..)-- -- ** Helper functions for implementing arbitrary- , arbitrarySizedIntegral -- :: Integral a => Gen a- , arbitraryBoundedIntegral -- :: (Bounded a, Integral a) => Gen a- , arbitrarySizedBoundedIntegral -- :: (Bounded a, Integral a) => Gen a- , arbitrarySizedFractional -- :: Fractional a => Gen a- , arbitraryBoundedRandom -- :: (Bounded a, Random a) => Gen a- , 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]- , recursivelyShrink -- :: (Generic a, RecursivelyShrink (Rep a)) => a -> [a]-#endif- , shrinkNothing -- :: a -> [a]- , shrinkList -- :: (a -> [a]) -> [a] -> [[a]]- , shrinkIntegral -- :: Integral a => a -> [a]- , shrinkRealFrac -- :: RealFrac a => a -> [a]- , shrinkRealFracToInteger -- :: RealFrac a => a -> [a]- -- ** Helper functions for implementing coarbitrary- , coarbitraryIntegral -- :: Integral a => a -> Gen b -> Gen b- , coarbitraryReal -- :: Real a => a -> Gen b -> Gen b- , coarbitraryShow -- :: Show a => a -> Gen b -> Gen b- , coarbitraryEnum -- :: Enum a => a -> Gen b -> Gen b- , (><)-- -- ** Generators which use arbitrary- , vector -- :: Arbitrary a => Int -> Gen [a]- , orderedList -- :: (Ord a, Arbitrary a) => Gen [a]- , infiniteList -- :: Arbitrary a => Gen [a]- )- where------------------------------------------------------------------------------- imports--import System.Random(Random)-import Test.QuickCheck.Gen-import Test.QuickCheck.Gen.Unsafe--{--import Data.Generics- ( (:*:)(..)- , (:+:)(..)- , Unit(..)- )--}--import Data.Char- ( chr- , ord- , isLower- , isUpper- , toLower- , isDigit- , isSpace- )--#ifndef NO_FIXED-import Data.Fixed- ( Fixed- , HasResolution- )-#endif--import Data.Ratio- ( Ratio- , (%)- , numerator- , denominator- )--import Data.Complex- ( Complex((:+)) )--import Data.List- ( sort- , nub- )--import Control.Monad- ( liftM- , liftM2- , liftM3- , liftM4- , liftM5- )--import Data.Int(Int8, Int16, Int32, Int64)-import Data.Word(Word, Word8, Word16, Word32, Word64)--#ifndef NO_GENERICS-import GHC.Generics-import Data.Typeable-#endif------------------------------------------------------------------------------- ** class Arbitrary---- | Random generation and shrinking of values.-class Arbitrary a where- -- | A generator for values of the given type.- arbitrary :: Gen a- arbitrary = error "no default generator"-- -- | Produces a (possibly) empty list of all the possible- -- immediate shrinks of the given value. The default implementation- -- returns the empty list, so will not try to shrink the value.- --- -- Most implementations of 'shrink' should try at least three things:- --- -- 1. Shrink a term to any of its immediate subterms.- --- -- 2. Recursively apply 'shrink' to all immediate subterms.- --- -- 3. Type-specific shrinkings such as replacing a constructor by a- -- simpler constructor.- --- -- For example, suppose we have the following implementation of binary trees:- --- -- > data Tree a = Nil | Branch a (Tree a) (Tree a)- --- -- We can then define 'shrink' as follows:- --- -- > shrink Nil = []- -- > shrink (Branch x l r) =- -- > -- shrink Branch to Nil- -- > [Nil] ++- -- > -- shrink to subterms- -- > [l, r] ++- -- > -- recursively shrink subterms- -- > [Branch x' l' r' | (x', l', r') <- shrink (x, l, r)]- --- -- There are a couple of subtleties here:- --- -- * QuickCheck tries the shrinking candidates in the order they- -- appear in the list, so we put more aggressive shrinking steps- -- (such as replacing the whole tree by @Nil@) before smaller- -- ones (such as recursively shrinking the subtrees).- --- -- * It is tempting to write the last line as- -- @[Branch x' l' r' | x' <- shrink x, l' <- shrink l, r' <- shrink r]@- -- but this is the /wrong thing/! It will force QuickCheck to shrink- -- @x@, @l@ and @r@ in tandem, and shrinking will stop once /one/ of- -- the three is fully shrunk.- --- -- There is a fair bit of boilerplate in the code above.- -- We can avoid it with the help of some generic functions;- -- note that these only work on GHC 7.2 and above.- -- The function 'genericShrink' tries shrinking a term to all of its- -- subterms and, failing that, recursively shrinks the subterms.- -- Using it, we can define 'shrink' as:- --- -- > shrink x = shrinkToNil x ++ genericShrink x- -- > where- -- > shrinkToNil Nil = []- -- > shrinkToNil (Branch _ l r) = [Nil]- --- -- 'genericShrink' is a combination of 'subterms', which shrinks- -- a term to any of its subterms, and 'recursivelyShrink', which shrinks- -- all subterms of a term. These may be useful if you need a bit more- -- control over shrinking than 'genericShrink' gives you.- --- -- A final gotcha: we cannot define 'shrink' as simply @'shrink' x = Nil:'genericShrink' x@- -- as this shrinks @Nil@ to @Nil@, and shrinking will go into an- -- 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- -- special invariants, you will need to check that 'genericShrink' can't break those invariants.- shrink :: a -> [a]- shrink _ = []--#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 x = subterms x ++ recursivelyShrink x---- | Recursively shrink all immediate subterms.-recursivelyShrink :: (Generic a, RecursivelyShrink (Rep a)) => a -> [a]-recursivelyShrink = map to . grecursivelyShrink . from--class RecursivelyShrink f where- grecursivelyShrink :: f a -> [f a]--instance (RecursivelyShrink f, RecursivelyShrink g) => RecursivelyShrink (f :*: g) where- grecursivelyShrink (x :*: y) =- [x' :*: y | x' <- grecursivelyShrink x] ++- [x :*: y' | y' <- grecursivelyShrink y]--instance (RecursivelyShrink f, RecursivelyShrink g) => RecursivelyShrink (f :+: g) where- grecursivelyShrink (L1 x) = map L1 (grecursivelyShrink x)- grecursivelyShrink (R1 x) = map R1 (grecursivelyShrink x)--instance RecursivelyShrink f => RecursivelyShrink (M1 i c f) where- grecursivelyShrink (M1 x) = map M1 (grecursivelyShrink x)--instance Arbitrary a => RecursivelyShrink (K1 i a) where- grecursivelyShrink (K1 x) = map K1 (shrink x)--instance RecursivelyShrink U1 where- grecursivelyShrink U1 = []---- | All immediate subterms of a term.-subterms :: (Generic a, Typeable a, Subterms (Rep 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--instance (Subterms f, Subterms g) => Subterms (f :+: g) where- gsubterms (L1 x) = gsubterms x- gsubterms (R1 x) = gsubterms x--instance Subterms f => Subterms (M1 i c f) where- gsubterms (M1 x) = gsubterms x--instance Typeable a => Subterms (K1 i a) where- gsubterms (K1 x) =- case cast x of- Nothing -> []- Just y -> [y]--instance Subterms U1 where- gsubterms U1 = []--#endif---- instances--instance (CoArbitrary a, Arbitrary b) => Arbitrary (a -> b) where- arbitrary = promote (`coarbitrary` arbitrary)--instance Arbitrary () where- arbitrary = return ()--instance Arbitrary Bool where- arbitrary = choose (False,True)- shrink True = [False]- shrink False = []--instance Arbitrary Ordering where- arbitrary = elements [LT, EQ, GT]- shrink GT = [EQ, LT]- shrink LT = [EQ]- shrink EQ = []--instance Arbitrary a => Arbitrary (Maybe a) where- arbitrary = frequency [(1, return Nothing), (3, liftM Just arbitrary)]-- shrink (Just x) = Nothing : [ Just x' | x' <- shrink x ]- shrink _ = []--instance (Arbitrary a, Arbitrary b) => Arbitrary (Either a b) where- arbitrary = oneof [liftM Left arbitrary, liftM Right arbitrary]-- shrink (Left x) = [ Left x' | x' <- shrink x ]- shrink (Right y) = [ Right y' | y' <- shrink y ]--instance Arbitrary a => Arbitrary [a] where- arbitrary = sized $ \n ->- do k <- choose (0,n)- sequence [ arbitrary | _ <- [1..k] ]-- shrink xs = shrinkList shrink xs---- | Shrink a list of values given a shrinking function for individual values.-shrinkList :: (a -> [a]) -> [a] -> [[a]]-shrinkList shr xs = concat [ removes k n xs | k <- takeWhile (>0) (iterate (`div`2) n) ]- ++ shrinkOne xs- where- n = length xs-- shrinkOne [] = []- shrinkOne (x:xs) = [ x':xs | x' <- shr x ]- ++ [ x:xs' | xs' <- shrinkOne xs ]-- removes k n xs- | k > n = []- | null xs2 = [[]]- | otherwise = xs2 : map (xs1 ++) (removes k (n-k) xs2)- where- xs1 = take k xs- xs2 = drop k xs--{-- -- "standard" definition for lists:- shrink [] = []- shrink (x:xs) = [ xs ]- ++ [ x:xs' | xs' <- shrink xs ]- ++ [ x':xs | x' <- shrink x ]--}--instance (Integral a, Arbitrary a) => Arbitrary (Ratio a) where- arbitrary = arbitrarySizedFractional- shrink = shrinkRealFracToInteger--instance (RealFloat a, Arbitrary a) => Arbitrary (Complex a) where- arbitrary = liftM2 (:+) arbitrary arbitrary- shrink (x :+ y) = [ x' :+ y | x' <- shrink x ] ++- [ x :+ y' | y' <- shrink y ]--#ifndef NO_FIXED-instance HasResolution a => Arbitrary (Fixed a) where- arbitrary = arbitrarySizedFractional- shrink = shrinkRealFrac-#endif--instance (Arbitrary a, Arbitrary b)- => Arbitrary (a,b)- where- arbitrary = liftM2 (,) arbitrary arbitrary-- shrink (x, y) =- [ (x', y) | x' <- shrink x ]- ++ [ (x, y') | y' <- shrink y ]--instance (Arbitrary a, Arbitrary b, Arbitrary c)- => Arbitrary (a,b,c)- where- arbitrary = liftM3 (,,) arbitrary arbitrary arbitrary-- shrink (x, y, z) =- [ (x', y', z')- | (x', (y', z')) <- shrink (x, (y, z)) ]--instance (Arbitrary a, Arbitrary b, Arbitrary c, Arbitrary d)- => Arbitrary (a,b,c,d)- where- arbitrary = liftM4 (,,,) arbitrary arbitrary arbitrary arbitrary-- shrink (w, x, y, z) =- [ (w', x', y', z')- | (w', (x', (y', z'))) <- shrink (w, (x, (y, z))) ]--instance (Arbitrary a, Arbitrary b, Arbitrary c, Arbitrary d, Arbitrary e)- => Arbitrary (a,b,c,d,e)- where- arbitrary = liftM5 (,,,,) arbitrary arbitrary arbitrary arbitrary arbitrary-- shrink (v, w, x, y, z) =- [ (v', w', x', y', z')- | (v', (w', (x', (y', z')))) <- shrink (v, (w, (x, (y, z)))) ]---- typical instance for primitive (numerical) types--instance Arbitrary Integer where- arbitrary = arbitrarySizedIntegral- shrink = shrinkIntegral--instance Arbitrary Int where- arbitrary = arbitrarySizedIntegral- shrink = shrinkIntegral--instance Arbitrary Int8 where- arbitrary = arbitrarySizedBoundedIntegral- shrink = shrinkIntegral--instance Arbitrary Int16 where- arbitrary = arbitrarySizedBoundedIntegral- shrink = shrinkIntegral--instance Arbitrary Int32 where- arbitrary = arbitrarySizedBoundedIntegral- shrink = shrinkIntegral--instance Arbitrary Int64 where- arbitrary = arbitrarySizedBoundedIntegral- shrink = shrinkIntegral--instance Arbitrary Word where- arbitrary = arbitrarySizedBoundedIntegral- shrink = shrinkIntegral--instance Arbitrary Word8 where- arbitrary = arbitrarySizedBoundedIntegral- shrink = shrinkIntegral--instance Arbitrary Word16 where- arbitrary = arbitrarySizedBoundedIntegral- shrink = shrinkIntegral--instance Arbitrary Word32 where- arbitrary = arbitrarySizedBoundedIntegral- shrink = shrinkIntegral--instance Arbitrary Word64 where- arbitrary = arbitrarySizedBoundedIntegral- shrink = shrinkIntegral--instance Arbitrary Char where- arbitrary = chr `fmap` oneof [choose (0,127), choose (0,255)]- shrink c = filter (<. c) $ nub- $ ['a','b','c']- ++ [ toLower c | isUpper c ]- ++ ['A','B','C']- ++ ['1','2','3']- ++ [' ','\n']- where- a <. b = stamp a < stamp b- stamp a = ( (not (isLower a)- , not (isUpper a)- , not (isDigit a))- , (not (a==' ')- , not (isSpace a)- , a)- )--instance Arbitrary Float where- arbitrary = arbitrarySizedFractional- shrink = shrinkRealFrac--instance Arbitrary Double where- arbitrary = arbitrarySizedFractional- shrink = shrinkRealFrac---- ** Helper functions for implementing arbitrary---- | Generates an integral number. The number can be positive or negative--- and its maximum absolute value depends on the size parameter.-arbitrarySizedIntegral :: Integral a => Gen a-arbitrarySizedIntegral =- sized $ \n ->- inBounds fromInteger (choose (-toInteger n, toInteger 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.-arbitrarySizedFractional :: Fractional a => Gen a-arbitrarySizedFractional =- sized $ \n ->- let n' = toInteger n in- do a <- choose ((-n') * precision, n' * precision)- b <- choose (1, precision)- return (fromRational (a % b))- where- precision = 9999999999999 :: Integer---- Useful for getting at minBound and maxBound without having to--- fiddle around with asTypeOf.-withBounds :: Bounded a => (a -> a -> Gen a) -> Gen a-withBounds k = k minBound maxBound---- | Generates an integral number. The number is chosen uniformly from--- the entire range of the type. You may want to use--- 'arbitrarySizedBoundedIntegral' instead.-arbitraryBoundedIntegral :: (Bounded a, Integral a) => Gen a-arbitraryBoundedIntegral =- withBounds $ \mn mx ->- do n <- choose (toInteger mn, toInteger mx)- return (fromInteger n)---- | Generates an element of a bounded type. The element is--- chosen from the entire range of the type.-arbitraryBoundedRandom :: (Bounded a, Random a) => Gen a-arbitraryBoundedRandom = choose (minBound,maxBound)---- | Generates an element of a bounded enumeration.-arbitraryBoundedEnum :: (Bounded a, Enum a) => Gen a-arbitraryBoundedEnum =- withBounds $ \mn mx ->- do n <- choose (fromEnum mn, fromEnum mx)- return (toEnum n)---- | Generates an integral number from a bounded domain. The number is--- chosen from the entire range of the type, but small numbers are--- generated more often than big numbers. Inspired by demands from--- Phil Wadler.-arbitrarySizedBoundedIntegral :: (Bounded a, Integral a) => Gen a-arbitrarySizedBoundedIntegral =- withBounds $ \mn mx ->- sized $ \s ->- do let bits n | n `quot` 2 == 0 = 0- | otherwise = 1 + bits (n `quot` 2)- k = 2^(s*(bits mn `max` bits mx `max` 40) `div` 100)- n <- choose (toInteger mn `max` (-k), toInteger mx `min` k)- return (fromInteger n)---- ** Helper functions for implementing shrink---- | Returns no shrinking alternatives.-shrinkNothing :: a -> [a]-shrinkNothing _ = []---- | Shrink an integral number.-shrinkIntegral :: Integral a => a -> [a]-shrinkIntegral x =- nub $- [ -x- | x < 0, -x > x- ] ++- [ x'- | x' <- takeWhile (<< x) (0:[ x - i | i <- tail (iterate (`quot` 2) x) ])- ]- where- -- a << b is "morally" abs a < abs b, but taking care of overflow.- a << b = case (a >= 0, b >= 0) of- (True, True) -> a < b- (False, False) -> a > b- (True, False) -> a + b < 0- (False, True) -> a + b > 0---- | Shrink a fraction, but only shrink to integral values.-shrinkRealFracToInteger :: RealFrac a => a -> [a]-shrinkRealFracToInteger x =- nub $- [ -x- | x < 0- ] ++- map fromInteger (shrinkIntegral (truncate x))---- | Shrink a fraction.-shrinkRealFrac :: RealFrac a => a -> [a]-shrinkRealFrac x =- nub $- shrinkRealFracToInteger x ++- [ x - x'- | x' <- take 20 (iterate (/ 2) x)- , (x - x') << x ]- where- a << b = abs a < abs b------------------------------------------------------------------------------- ** CoArbitrary---- | Used for random generation of functions.-class CoArbitrary a where- -- | Used to generate a function of type @a -> b@.- -- The first argument is a value, the second a generator.- -- You should use 'variant' to perturb the random generator;- -- the goal is that different values for the first argument will- -- lead to different calls to 'variant'. An example will help:- --- -- @- -- instance CoArbitrary a => CoArbitrary [a] where- -- coarbitrary [] = 'variant' 0- -- coarbitrary (x:xs) = 'variant' 1 . coarbitrary (x,xs)- -- @-- coarbitrary :: a -> Gen b -> Gen b--{-# DEPRECATED (><) "Use ordinary function composition instead" #-}--- | Combine two generator perturbing functions, for example the--- results of calls to 'variant' or 'coarbitrary'.-(><) :: (Gen a -> Gen a) -> (Gen a -> Gen a) -> (Gen a -> Gen a)-(><) = (.)--instance (Arbitrary a, CoArbitrary b) => CoArbitrary (a -> b) where- coarbitrary f gen =- do xs <- arbitrary- coarbitrary (map f xs) gen--instance CoArbitrary () where- coarbitrary _ = id--instance CoArbitrary Bool where- coarbitrary False = variant 0- coarbitrary True = variant 1--instance CoArbitrary Ordering where- coarbitrary GT = variant 0- coarbitrary EQ = variant 1- coarbitrary LT = variant 2--instance CoArbitrary a => CoArbitrary (Maybe a) where- coarbitrary Nothing = variant 0- coarbitrary (Just x) = variant 1 . coarbitrary x--instance (CoArbitrary a, CoArbitrary b) => CoArbitrary (Either a b) where- coarbitrary (Left x) = variant 0 . coarbitrary x- coarbitrary (Right y) = variant 1 . coarbitrary y--instance CoArbitrary a => CoArbitrary [a] where- coarbitrary [] = variant 0- coarbitrary (x:xs) = variant 1 . coarbitrary (x,xs)--instance (Integral a, CoArbitrary a) => CoArbitrary (Ratio a) where- coarbitrary r = coarbitrary (numerator r,denominator r)--#ifndef NO_FIXED-instance HasResolution a => CoArbitrary (Fixed a) where- coarbitrary = coarbitraryReal-#endif--instance (RealFloat a, CoArbitrary a) => CoArbitrary (Complex a) where- coarbitrary (x :+ y) = coarbitrary x . coarbitrary y--instance (CoArbitrary a, CoArbitrary b)- => CoArbitrary (a,b)- where- coarbitrary (x,y) = coarbitrary x- . coarbitrary y--instance (CoArbitrary a, CoArbitrary b, CoArbitrary c)- => CoArbitrary (a,b,c)- where- coarbitrary (x,y,z) = coarbitrary x- . coarbitrary y- . coarbitrary z--instance (CoArbitrary a, CoArbitrary b, CoArbitrary c, CoArbitrary d)- => CoArbitrary (a,b,c,d)- where- coarbitrary (x,y,z,v) = coarbitrary x- . coarbitrary y- . coarbitrary z- . coarbitrary v--instance (CoArbitrary a, CoArbitrary b, CoArbitrary c, CoArbitrary d, CoArbitrary e)- => CoArbitrary (a,b,c,d,e)- where- coarbitrary (x,y,z,v,w) = coarbitrary x- . coarbitrary y- . coarbitrary z- . coarbitrary v- . coarbitrary w---- typical instance for primitive (numerical) types--instance CoArbitrary Integer where- coarbitrary = coarbitraryIntegral--instance CoArbitrary Int where- coarbitrary = coarbitraryIntegral--instance CoArbitrary Int8 where- coarbitrary = coarbitraryIntegral--instance CoArbitrary Int16 where- coarbitrary = coarbitraryIntegral--instance CoArbitrary Int32 where- coarbitrary = coarbitraryIntegral--instance CoArbitrary Int64 where- coarbitrary = coarbitraryIntegral--instance CoArbitrary Word where- coarbitrary = coarbitraryIntegral--instance CoArbitrary Word8 where- coarbitrary = coarbitraryIntegral--instance CoArbitrary Word16 where- coarbitrary = coarbitraryIntegral--instance CoArbitrary Word32 where- coarbitrary = coarbitraryIntegral--instance CoArbitrary Word64 where- coarbitrary = coarbitraryIntegral--instance CoArbitrary Char where- coarbitrary = coarbitrary . ord--instance CoArbitrary Float where- coarbitrary = coarbitraryReal--instance CoArbitrary Double where- coarbitrary = coarbitraryReal---- ** Helpers for implementing coarbitrary---- | A 'coarbitrary' implementation for integral numbers.-coarbitraryIntegral :: Integral a => a -> Gen b -> Gen b-coarbitraryIntegral = variant---- | A 'coarbitrary' implementation for real numbers.-coarbitraryReal :: Real a => a -> Gen b -> Gen b-coarbitraryReal x = coarbitrary (toRational x)---- | 'coarbitrary' helper for lazy people :-).-coarbitraryShow :: Show a => a -> Gen b -> Gen b-coarbitraryShow x = coarbitrary (show x)---- | A 'coarbitrary' implementation for enums.-coarbitraryEnum :: Enum a => a -> Gen b -> Gen b-coarbitraryEnum = variant . fromEnum------------------------------------------------------------------------------- ** arbitrary generators---- these are here and not in Gen because of the Arbitrary class constraint---- | Generates a list of a given length.-vector :: Arbitrary a => Int -> Gen [a]-vector k = vectorOf k arbitrary---- | Generates an ordered list of a given length.-orderedList :: (Ord a, Arbitrary a) => Gen [a]-orderedList = sort `fmap` arbitrary---- | Generate an infinite list.-infiniteList :: Arbitrary a => Gen [a]-infiniteList = infiniteListOf arbitrary------------------------------------------------------------------------------- the end.
− Test/QuickCheck/Exception.hs
@@ -1,128 +0,0 @@--- | Throwing and catching exceptions. Internal QuickCheck module.---- Hide away the nasty implementation-specific ways of catching--- exceptions behind a nice API. The main trouble is catching ctrl-C.--{-# LANGUAGE CPP #-}-module Test.QuickCheck.Exception where--#if !defined(__GLASGOW_HASKELL__) || (__GLASGOW_HASKELL__ < 609)-#define OLD_EXCEPTIONS-#endif--#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ >= 607-#define GHC_INTERRUPT--#if __GLASGOW_HASKELL__ < 613-#define GHCI_INTERRUPTED_EXCEPTION-#endif--#if __GLASGOW_HASKELL__ >= 700-#define NO_BASE_3-#endif-#endif--#if defined(NO_EXCEPTIONS)-#elif defined(OLD_EXCEPTIONS) || defined(NO_BASE_3)-import qualified Control.Exception as E-#else-import qualified Control.Exception.Extensible as E-#endif--#if defined(GHC_INTERRUPT)-#if defined(GHCI_INTERRUPTED_EXCEPTION)-import Panic(GhcException(Interrupted))-#endif-import Data.Typeable-#if defined(OLD_EXCEPTIONS)-import Data.Dynamic-#endif-#endif--#if defined(NO_EXCEPTIONS)-type AnException = ()-#elif defined(OLD_EXCEPTIONS)-type AnException = E.Exception-#else-type AnException = E.SomeException-#endif--#ifdef NO_EXCEPTIONS-tryEvaluate :: a -> IO (Either AnException a)-tryEvaluate x = return (Right x)--tryEvaluateIO :: IO a -> IO (Either AnException a)-tryEvaluateIO m = fmap Right m--isInterrupt :: AnException -> Bool-isInterrupt _ = False--discard :: a-discard = error "'discard' not supported, since your Haskell system can't catch exceptions"--isDiscard :: AnException -> Bool-isDiscard _ = False--finally :: IO a -> IO b -> IO a-finally mx my = do- x <- mx- my- return x--#else------------------------------------------------------------------------------ try evaluate--tryEvaluate :: a -> IO (Either AnException a)-tryEvaluate x = tryEvaluateIO (return x)--tryEvaluateIO :: IO a -> IO (Either AnException a)-tryEvaluateIO m = E.try (m >>= E.evaluate)---tryEvaluateIO m = Right `fmap` m---- Test if an exception was a ^C.--- QuickCheck won't try to shrink an interrupted test case.-isInterrupt :: AnException -> Bool--#if defined(GHC_INTERRUPT)-#if defined(OLD_EXCEPTIONS)-isInterrupt (E.DynException e) = fromDynamic e == Just Interrupted-isInterrupt _ = False-#elif defined(GHCI_INTERRUPTED_EXCEPTION)-isInterrupt e =- E.fromException e == Just Interrupted || E.fromException e == Just E.UserInterrupt-#else-isInterrupt e = E.fromException e == Just E.UserInterrupt-#endif--#else /* !defined(GHC_INTERRUPT) */-isInterrupt _ = False-#endif---- | A special exception that makes QuickCheck discard the test case.--- Normally you should use '==>', but if for some reason this isn't--- possible (e.g. you are deep inside a generator), use 'discard'--- instead.-discard :: a--isDiscard :: AnException -> Bool-(discard, isDiscard) = (E.throw (E.ErrorCall msg), isDiscard)- where- msg = "DISCARD. " ++- "You should not see this exception, it is internal to QuickCheck."-#if defined(OLD_EXCEPTIONS)- isDiscard (E.ErrorCall msg') = msg' == msg- isDiscard _ = False-#else- isDiscard (E.SomeException e) =- case cast e of- Just (E.ErrorCall msg') -> msg' == msg- _ -> False-#endif--finally :: IO a -> IO b -> IO a-finally = E.finally-#endif------------------------------------------------------------------------------- the end.
− Test/QuickCheck/Function.hs
@@ -1,283 +0,0 @@-{-# LANGUAGE TypeOperators, GADTs #-}--- | Generation of random shrinkable, showable functions.--- See the paper \"Shrinking and showing functions\" by Koen Claessen.------ Example of use:------ >>> :{--- >>> let prop :: Fun String Integer -> Bool--- >>> prop (Fun _ f) = f "monkey" == f "banana" || f "banana" == f "elephant"--- >>> :}--- >>> quickCheck prop--- *** Failed! Falsifiable (after 3 tests and 134 shrinks):--- {"elephant"->1, "monkey"->1, _->0}------ To generate random values of type @'Fun' a b@,--- you must have an instance @'Function' a@.--- If your type has a 'Show' instance, you can use 'functionShow' to write the instance; otherwise,--- use 'functionMap' to give a bijection between your type and a type that is already an instance of 'Function'.--- See the @'Function' [a]@ instance for an example of the latter.-module Test.QuickCheck.Function- ( Fun(..)- , apply- , (:->)- , Function(..)- , functionMap- , functionShow- )- where------------------------------------------------------------------------------- imports--import Test.QuickCheck.Arbitrary-import Test.QuickCheck.Poly--import Data.Char-import Data.Word-import Data.List( intersperse )-import Data.Maybe( fromJust )------------------------------------------------------------------------------- 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)- Unit :: c -> (() :-> c)- Nil :: a :-> c- Table :: Eq a => [(a,c)] -> (a :-> c)- Map :: (a -> b) -> (b -> a) -> (b :-> c) -> (a :-> c)--instance Functor ((:->) a) where- fmap f (Pair p) = Pair (fmap (fmap f) p)- fmap f (p:+:q) = fmap f p :+: fmap f q- fmap f (Unit c) = Unit (f c)- fmap f Nil = Nil- fmap f (Table xys) = Table [ (x,f y) | (x,y) <- xys ]- fmap f (Map g h p) = Map g h (fmap f p)--instance (Show a, Show b) => Show (a:->b) where- show p = showFunction p Nothing---- only use this on finite functions-showFunction :: (Show a, Show b) => (a :-> b) -> Maybe b -> String-showFunction p md =- "{" ++ concat (intersperse ", " ( [ show x ++ "->" ++ show c- | (x,c) <- table p- ]- ++ [ "_->" ++ show d- | Just d <- [md]- ] )) ++ "}"---- turning a concrete function into an abstract function (with a default result)-abstract :: (a :-> c) -> c -> (a -> c)-abstract (Pair p) d (x,y) = abstract (fmap (\q -> abstract q d y) p) d x-abstract (p :+: q) d exy = either (abstract p d) (abstract q d) exy-abstract (Unit c) _ _ = c-abstract Nil d _ = d-abstract (Table xys) d x = head ([y | (x',y) <- xys, x == x'] ++ [d])-abstract (Map g _ p) d x = abstract p d (g x)---- generating a table from a concrete function-table :: (a :-> c) -> [(a,c)]-table (Pair p) = [ ((x,y),c) | (x,q) <- table p, (y,c) <- table q ]-table (p :+: q) = [ (Left x, c) | (x,c) <- table p ]- ++ [ (Right y,c) | (y,c) <- table q ]-table (Unit c) = [ ((), c) ]-table Nil = []-table (Table xys) = xys-table (Map _ h p) = [ (h x, c) | (x,c) <- table p ]------------------------------------------------------------------------------- Function--class Function a where- function :: (a->b) -> (a:->b)---- basic instances--instance Function () where- function f = Unit (f ())--instance Function Word8 where- function f = Table [(x,f x) | x <- [0..255]]--instance (Function a, Function b) => Function (a,b) where- function f = Pair (function `fmap` function (curry f))--instance (Function a, Function b) => Function (Either a b) where- function f = function (f . Left) :+: function (f . Right)---- tuple convenience instances--instance (Function a, Function b, Function c) => Function (a,b,c) where- function = functionMap (\(a,b,c) -> (a,(b,c))) (\(a,(b,c)) -> (a,b,c))--instance (Function a, Function b, Function c, Function d) => Function (a,b,c,d) where- function = functionMap (\(a,b,c,d) -> (a,(b,c,d))) (\(a,(b,c,d)) -> (a,b,c,d))--instance (Function a, Function b, Function c, Function d, Function e) => Function (a,b,c,d,e) where- function = functionMap (\(a,b,c,d,e) -> (a,(b,c,d,e))) (\(a,(b,c,d,e)) -> (a,b,c,d,e))--instance (Function a, Function b, Function c, Function d, Function e, Function f) => Function (a,b,c,d,e,f) where- function = functionMap (\(a,b,c,d,e,f) -> (a,(b,c,d,e,f))) (\(a,(b,c,d,e,f)) -> (a,b,c,d,e,f))--instance (Function a, Function b, Function c, Function d, Function e, Function f, Function g) => Function (a,b,c,d,e,f,g) where- function = functionMap (\(a,b,c,d,e,f,g) -> (a,(b,c,d,e,f,g))) (\(a,(b,c,d,e,f,g)) -> (a,b,c,d,e,f,g))---- other instances--functionMap :: Function b => (a->b) -> (b->a) -> (a->c) -> (a:->c)-functionMap g h f = Map g h (function (\b -> f (h b)))--functionShow :: (Show a, Read a) => (a->c) -> (a:->c)-functionShow f = functionMap show read f--instance Function a => Function [a] where- function = functionMap g h- where- g [] = Left ()- g (x:xs) = Right (x,xs)-- h (Left _) = []- h (Right (x,xs)) = x:xs--instance Function a => Function (Maybe a) where- function = functionMap g h- where- g Nothing = Left ()- g (Just x) = Right x-- h (Left _) = Nothing- h (Right x) = Just x--instance Function Bool where- function = functionMap g h- where- g False = Left ()- g True = Right ()-- h (Left _) = False- h (Right _) = True--instance Function Integer where- function = functionMap gInteger hInteger- where- gInteger n | n < 0 = Left (gNatural (abs n - 1))- | otherwise = Right (gNatural n)-- hInteger (Left ws) = -(hNatural ws + 1)- hInteger (Right ws) = hNatural ws-- gNatural 0 = []- gNatural n = (fromIntegral (n `mod` 256) :: Word8) : gNatural (n `div` 256)-- hNatural [] = 0- hNatural (w:ws) = fromIntegral w + 256 * hNatural ws--instance Function Int where- function = functionMap fromIntegral fromInteger--instance Function Char where- function = functionMap ord' chr'- where- ord' c = fromIntegral (ord c) :: Word8- chr' n = chr (fromIntegral n)---- poly instances--instance Function A where- function = functionMap unA A--instance Function B where- function = functionMap unB B--instance Function C where- function = functionMap unC C--instance Function OrdA where- function = functionMap unOrdA OrdA--instance Function OrdB where- function = functionMap unOrdB OrdB--instance Function OrdC where- function = functionMap unOrdC OrdC---- instance Arbitrary--instance (Function a, CoArbitrary a, Arbitrary b) => Arbitrary (a:->b) where- arbitrary = function `fmap` arbitrary- shrink = shrinkFun shrink------------------------------------------------------------------------------- shrinking--shrinkFun :: (c -> [c]) -> (a :-> c) -> [a :-> c]-shrinkFun shr (Pair p) =- [ pair p' | p' <- shrinkFun (\q -> shrinkFun shr q) p ]- where- pair Nil = Nil- pair p = Pair p--shrinkFun shr (p :+: q) =- [ p .+. Nil | not (isNil q) ] ++- [ Nil .+. q | not (isNil p) ] ++- [ p .+. q' | q' <- shrinkFun shr q ] ++- [ p' .+. q | p' <- shrinkFun shr p ]- where- isNil :: (a :-> b) -> Bool- isNil Nil = True- isNil _ = False-- Nil .+. Nil = Nil- p .+. q = p :+: q--shrinkFun shr (Unit c) =- [ Nil ] ++- [ Unit c' | c' <- shr c ]--shrinkFun shr (Table xys) =- [ table xys' | xys' <- shrinkList shrXy xys ]- where- shrXy (x,y) = [(x,y') | y' <- shr y]-- table [] = Nil- table xys = Table xys--shrinkFun shr Nil =- []--shrinkFun shr (Map g h p) =- [ mapp g h p' | p' <- shrinkFun shr p ]- where- mapp g h Nil = Nil- mapp g h p = Map g h p------------------------------------------------------------------------------- the Fun modifier--data Fun a b = Fun (a :-> b, b) (a -> b)--mkFun :: (a :-> b) -> b -> Fun a b-mkFun p d = Fun (p,d) (abstract p d)--apply :: Fun a b -> (a -> b)-apply (Fun _ f) = f--instance (Show a, Show b) => Show (Fun a b) where- show (Fun (p,d) _) = showFunction p (Just d)--instance (Function a, CoArbitrary a, Arbitrary b) => Arbitrary (Fun a b) where- arbitrary =- do p <- arbitrary- d <- arbitrary- return (mkFun p d)-- shrink (Fun (p,d) _) =- [ mkFun p' d' | (p', d') <- shrink (p, d) ]------------------------------------------------------------------------------- the end.
− Test/QuickCheck/Gen.hs
@@ -1,181 +0,0 @@-{-# LANGUAGE CPP #-}-#ifndef NO_ST_MONAD-{-# LANGUAGE Rank2Types #-}-#endif--- | Test case generation.-module Test.QuickCheck.Gen where------------------------------------------------------------------------------- imports--import System.Random- ( Random- , StdGen- , randomR- , split- , newStdGen- )--import Control.Monad- ( liftM- , ap- )--import Control.Applicative- ( Applicative(..)- )--import Test.QuickCheck.Random------------------------------------------------------------------------------- ** Generator type---- | A generator for values of type @a@.-newtype Gen a = MkGen{- unGen :: QCGen -> Int -> a -- ^ Run the generator on a particular seed.- -- If you just want to get a random value out, consider using 'generate'.- }--instance Functor Gen where- fmap f (MkGen h) =- MkGen (\r n -> f (h r n))--instance Applicative Gen where- pure = return- (<*>) = ap--instance Monad Gen where- return x =- MkGen (\_ _ -> x)-- MkGen m >>= k =- MkGen (\r n ->- let (r1,r2) = split r- MkGen m' = k (m r1 n)- in m' r2 n- )------------------------------------------------------------------------------- ** Primitive generator combinators---- | Modifies a generator using an integer seed.-variant :: Integral n => n -> Gen a -> Gen a-variant k (MkGen g) = MkGen (\r n -> g (variantQCGen k r) n)---- | Used to construct generators that depend on the size parameter.-sized :: (Int -> Gen a) -> Gen a-sized f = MkGen (\r n -> let MkGen m = f n in m r n)---- | 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)---- | 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)---- | Run a generator.-generate :: Gen a -> IO a-generate (MkGen g) =- do r <- newQCGen- return (g r 30)---- | Generates some example values.-sample' :: Gen a -> IO [a]-sample' g =- generate (sequence [ resize n g | n <- [0,2..20] ])---- | Generates some example values and prints them to 'stdout'.-sample :: Show a => Gen a -> IO ()-sample g =- do cases <- sample' g- mapM_ print cases------------------------------------------------------------------------------- ** Common generator combinators---- | Generates a value that satisfies a predicate.-suchThat :: Gen a -> (a -> Bool) -> Gen a-gen `suchThat` p =- do mx <- gen `suchThatMaybe` p- case mx of- Just x -> return x- Nothing -> sized (\n -> resize (n+1) (gen `suchThat` p))---- | Tries to generate a value that satisfies a predicate.-suchThatMaybe :: Gen a -> (a -> Bool) -> Gen (Maybe a)-gen `suchThatMaybe` p = sized (try 0 . max 1)- where- try _ 0 = return Nothing- try k n = do x <- resize (2*k+n) gen- if p x then return (Just x) else try (k+1) (n-1)---- | Randomly uses one of the given generators. The input list--- must be non-empty.-oneof :: [Gen a] -> Gen a-oneof [] = error "QuickCheck.oneof used with empty list"-oneof gs = choose (0,length gs - 1) >>= (gs !!)---- | Chooses one of the given generators, with a weighted random distribution.--- The input list must be non-empty.-frequency :: [(Int, Gen a)] -> Gen a-frequency [] = error "QuickCheck.frequency used with empty list"-frequency xs0 = choose (1, tot) >>= (`pick` xs0)- where- tot = sum (map fst xs0)-- pick n ((k,x):xs)- | n <= k = x- | otherwise = pick (n-k) xs- pick _ _ = error "QuickCheck.pick used with empty list"---- | Generates one of the given values. The input list must be non-empty.-elements :: [a] -> Gen a-elements [] = error "QuickCheck.elements used with empty list"-elements xs = (xs !!) `fmap` choose (0, length xs - 1)---- | 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.--- The input list must be non-empty.-growingElements :: [a] -> Gen a-growingElements [] = error "QuickCheck.growingElements used with empty list"-growingElements xs = sized $ \n -> elements (take (1 `max` size n) xs)- where- k = length xs- mx = 100- log' = round . log . toDouble- size n = (log' n + 1) * k `div` log' mx- toDouble = fromIntegral :: Int -> Double--{- WAS:-growingElements xs = sized $ \n -> elements (take (1 `max` (n * k `div` 100)) xs)- where- k = length xs--}---- | Generates a list of random length. The maximum length depends on the--- size parameter.-listOf :: Gen a -> Gen [a]-listOf gen = sized $ \n ->- do k <- choose (0,n)- vectorOf k gen---- | Generates a non-empty list of random length. The maximum length--- depends on the size parameter.-listOf1 :: Gen a -> Gen [a]-listOf1 gen = sized $ \n ->- do k <- choose (1,1 `max` n)- vectorOf k gen---- | Generates a list of the given length.-vectorOf :: Int -> Gen a -> Gen [a]-vectorOf k gen = sequence [ gen | _ <- [1..k] ]---- | Generates an infinite list.-infiniteListOf :: Gen a -> Gen [a]-infiniteListOf gen = sequence (repeat gen)------------------------------------------------------------------------------- the end.
− Test/QuickCheck/Gen/Unsafe.hs
@@ -1,51 +0,0 @@-{-# LANGUAGE CPP #-}-#ifndef NO_ST_MONAD-{-# 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.--- QuickCheck maintains the illusion that a 'Gen' is a probability--- distribution and does not allow you to distinguish two generators--- that have the same distribution.------ The functions in this module allow you to break this illusion by--- reusing the same random number seed twice. This is unsafe because--- by applying the same seed to two morally equal generators, you can--- see whether they are really equal or not.-module Test.QuickCheck.Gen.Unsafe where--import Test.QuickCheck.Gen-import Control.Monad---- | Promotes a monadic generator to a generator of monadic values.-promote :: Monad m => m (Gen a) -> Gen (m a)-promote m = do- eval <- delay- return (liftM eval m)---- | Randomly generates a function of type @'Gen' a -> a@, which--- you can then use to evaluate generators. Mostly useful in--- implementing 'promote'.-delay :: Gen (Gen a -> a)-delay = MkGen (\r n g -> unGen g r n)--#ifndef NO_ST_MONAD--- | A variant of 'delay' that returns a polymorphic evaluation function.--- Can be used in a pinch to generate polymorphic (rank-2) values:------ > genSelector :: Gen (a -> a -> a)--- > genSelector = elements [\x y -> x, \x y -> y]--- >--- > data Selector = Selector (forall a. a -> a -> a)--- > genPolySelector :: Gen Selector--- > genPolySelector = do--- > Capture eval <- capture--- > return (Selector (eval genSelector))-capture :: Gen Capture-capture = MkGen (\r n -> Capture (\g -> unGen g r n))--newtype Capture = Capture (forall a. Gen a -> a)-#endif
− Test/QuickCheck/Modifiers.hs
@@ -1,346 +0,0 @@-{-# LANGUAGE CPP #-}-#ifndef NO_SAFE_HASKELL-{-# LANGUAGE Trustworthy #-}-#endif-#ifndef NO_MULTI_PARAM_TYPE_CLASSES-{-# LANGUAGE MultiParamTypeClasses #-}-#endif-#ifndef NO_NEWTYPE_DERIVING-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-#endif--- | Modifiers for test data.------ These types do things such as restricting the kind of test data that can be generated.--- They can be pattern-matched on in properties as a stylistic--- alternative to using explicit quantification.------ Examples:------ @--- -- Functions cannot be shown (but see "Test.QuickCheck.Function")--- prop_TakeDropWhile ('Blind' p) (xs :: ['A']) =--- takeWhile p xs ++ dropWhile p xs == xs--- @------ @--- prop_TakeDrop ('NonNegative' n) (xs :: ['A']) =--- take n xs ++ drop n xs == xs--- @------ @--- -- cycle does not work for empty lists--- prop_Cycle ('NonNegative' n) ('NonEmpty' (xs :: ['A'])) =--- take n (cycle xs) == take n (xs ++ cycle xs)--- @------ @--- -- Instead of 'forAll' 'orderedList'--- prop_Sort ('Ordered' (xs :: ['OrdA'])) =--- sort xs == xs--- @-module Test.QuickCheck.Modifiers- (- -- ** Type-level modifiers for changing generator behavior- Blind(..)- , Fixed(..)- , OrderedList(..)- , NonEmptyList(..)- , Positive(..)- , NonZero(..)- , NonNegative(..)- , Large(..)- , Small(..)- , Smart(..)- , Shrink2(..)-#ifndef NO_MULTI_PARAM_TYPE_CLASSES- , Shrinking(..)- , ShrinkState(..)-#endif- )- where------------------------------------------------------------------------------- imports--import Test.QuickCheck.Gen-import Test.QuickCheck.Arbitrary--import Data.List- ( sort- )------------------------------------------------------------------------------- | @Blind x@: as x, but x does not have to be in the 'Show' class.-newtype Blind a = Blind {getBlind :: a}- deriving ( Eq, Ord-#ifndef NO_NEWTYPE_DERIVING- , Num, Integral, Real, Enum-#endif- )--instance Functor Blind where- fmap f (Blind x) = Blind (f x)--instance Show (Blind a) where- show _ = "(*)"--instance Arbitrary a => Arbitrary (Blind a) where- arbitrary = Blind `fmap` arbitrary-- shrink (Blind x) = [ Blind x' | x' <- shrink x ]------------------------------------------------------------------------------- | @Fixed x@: as x, but will not be shrunk.-newtype Fixed a = Fixed {getFixed :: a}- deriving ( Eq, Ord, Show, Read-#ifndef NO_NEWTYPE_DERIVING- , Num, Integral, Real, Enum-#endif- )--instance Functor Fixed where- fmap f (Fixed x) = Fixed (f x)--instance Arbitrary a => Arbitrary (Fixed a) where- arbitrary = Fixed `fmap` arbitrary-- -- no shrink function------------------------------------------------------------------------------- | @Ordered xs@: guarantees that xs is ordered.-newtype OrderedList a = Ordered {getOrdered :: [a]}- deriving ( Eq, Ord, Show, Read )--instance Functor OrderedList where- fmap f (Ordered x) = Ordered (map f x)--instance (Ord a, Arbitrary a) => Arbitrary (OrderedList a) where- arbitrary = Ordered `fmap` orderedList-- shrink (Ordered xs) =- [ Ordered xs'- | xs' <- shrink xs- , sort xs' == xs'- ]------------------------------------------------------------------------------- | @NonEmpty xs@: guarantees that xs is non-empty.-newtype NonEmptyList a = NonEmpty {getNonEmpty :: [a]}- deriving ( Eq, Ord, Show, Read )--instance Functor NonEmptyList where- fmap f (NonEmpty x) = NonEmpty (map f x)--instance Arbitrary a => Arbitrary (NonEmptyList a) where- arbitrary = NonEmpty `fmap` (arbitrary `suchThat` (not . null))-- shrink (NonEmpty xs) =- [ NonEmpty xs'- | xs' <- shrink xs- , not (null xs')- ]------------------------------------------------------------------------------- | @Positive x@: guarantees that @x \> 0@.-newtype Positive a = Positive {getPositive :: a}- deriving ( Eq, Ord, Show, Read-#ifndef NO_NEWTYPE_DERIVING- , Enum-#endif- )--instance Functor Positive where- fmap f (Positive x) = Positive (f x)--instance (Num a, Ord a, Arbitrary a) => Arbitrary (Positive a) where- arbitrary =- ((Positive . abs) `fmap` (arbitrary `suchThat` (/= 0))) `suchThat` gt0- where gt0 (Positive x) = x > 0-- shrink (Positive x) =- [ Positive x'- | x' <- shrink x- , x' > 0- ]------------------------------------------------------------------------------- | @NonZero x@: guarantees that @x \/= 0@.-newtype NonZero a = NonZero {getNonZero :: a}- deriving ( Eq, Ord, Show, Read-#ifndef NO_NEWTYPE_DERIVING- , Enum-#endif- )--instance Functor NonZero where- fmap f (NonZero x) = NonZero (f x)--instance (Num a, Ord a, Arbitrary a) => Arbitrary (NonZero a) where- arbitrary = fmap NonZero $ arbitrary `suchThat` (/= 0)-- shrink (NonZero x) = [ NonZero x' | x' <- shrink x, x' /= 0 ]------------------------------------------------------------------------------- | @NonNegative x@: guarantees that @x \>= 0@.-newtype NonNegative a = NonNegative {getNonNegative :: a}- deriving ( Eq, Ord, Show, Read-#ifndef NO_NEWTYPE_DERIVING- , Enum-#endif- )--instance Functor NonNegative where- fmap f (NonNegative x) = NonNegative (f x)--instance (Num a, Ord a, Arbitrary a) => Arbitrary (NonNegative a) where- arbitrary =- (frequency- -- why is this distrbution like this?- [ (5, (NonNegative . abs) `fmap` arbitrary)- , (1, return (NonNegative 0))- ]- ) `suchThat` ge0- where ge0 (NonNegative x) = x >= 0-- shrink (NonNegative x) =- [ NonNegative x'- | x' <- shrink x- , x' >= 0- ]------------------------------------------------------------------------------- | @Large x@: by default, QuickCheck generates 'Int's drawn from a small--- range. @Large Int@ gives you values drawn from the entire range instead.-newtype Large a = Large {getLarge :: a}- deriving ( Eq, Ord, Show, Read-#ifndef NO_NEWTYPE_DERIVING- , Num, Integral, Real, Enum-#endif- )--instance Functor Large where- fmap f (Large x) = Large (f x)--instance (Integral a, Bounded a) => Arbitrary (Large a) where- arbitrary = fmap Large arbitrarySizedBoundedIntegral- shrink (Large x) = fmap Large (shrinkIntegral x)------------------------------------------------------------------------------- | @Small x@: generates values of @x@ drawn from a small range.--- The opposite of 'Large'.-newtype Small a = Small {getSmall :: a}- deriving ( Eq, Ord, Show, Read-#ifndef NO_NEWTYPE_DERIVING- , Num, Integral, Real, Enum-#endif- )--instance Functor Small where- fmap f (Small x) = Small (f x)--instance Integral a => Arbitrary (Small a) where- arbitrary = fmap Small arbitrarySizedIntegral- shrink (Small x) = map Small (shrinkIntegral x)------------------------------------------------------------------------------- | @Shrink2 x@: allows 2 shrinking steps at the same time when shrinking x-newtype Shrink2 a = Shrink2 {getShrink2 :: a}- deriving ( Eq, Ord, Show, Read-#ifndef NO_NEWTYPE_DERIVING- , Num, Integral, Real, Enum-#endif- )--instance Functor Shrink2 where- fmap f (Shrink2 x) = Shrink2 (f x)--instance Arbitrary a => Arbitrary (Shrink2 a) where- arbitrary =- Shrink2 `fmap` arbitrary-- shrink (Shrink2 x) =- [ Shrink2 y | y <- shrink_x ] ++- [ Shrink2 z- | y <- shrink_x- , z <- shrink y- ]- where- shrink_x = shrink x------------------------------------------------------------------------------- | @Smart _ x@: tries a different order when shrinking.-data Smart a =- Smart Int a--instance Functor Smart where- fmap f (Smart n x) = Smart n (f x)--instance Show a => Show (Smart a) where- showsPrec n (Smart _ x) = showsPrec n x--instance Arbitrary a => Arbitrary (Smart a) where- arbitrary =- do x <- arbitrary- return (Smart 0 x)-- shrink (Smart i x) = take i' ys `ilv` drop i' ys- where- ys = [ Smart j y | (j,y) <- [0..] `zip` shrink x ]- i' = 0 `max` (i-2)-- [] `ilv` bs = bs- as `ilv` [] = as- (a:as) `ilv` (b:bs) = a : b : (as `ilv` bs)--{-- shrink (Smart i x) = part0 ++ part2 ++ part1- where- ys = [ Smart i y | (i,y) <- [0..] `zip` shrink x ]- i' = 0 `max` (i-2)- k = i `div` 10-- part0 = take k ys- part1 = take (i'-k) (drop k ys)- part2 = drop i' ys--}-- -- drop a (drop b xs) == drop (a+b) xs | a,b >= 0- -- take a (take b xs) == take (a `min` b) xs- -- take a xs ++ drop a xs == xs-- -- take k ys ++ take (i'-k) (drop k ys) ++ drop i' ys- -- == take k ys ++ take (i'-k) (drop k ys) ++ drop (i'-k) (drop k ys)- -- == take k ys ++ take (i'-k) (drop k ys) ++ drop (i'-k) (drop k ys)- -- == take k ys ++ drop k ys- -- == ys--#ifndef NO_MULTI_PARAM_TYPE_CLASSES------------------------------------------------------------------------------ | @Shrinking _ x@: allows for maintaining a state during shrinking.-data Shrinking s a =- Shrinking s a--class ShrinkState s a where- shrinkInit :: a -> s- shrinkState :: a -> s -> [(a,s)]--instance Functor (Shrinking s) where- fmap f (Shrinking s x) = Shrinking s (f x)--instance Show a => Show (Shrinking s a) where- showsPrec n (Shrinking _ x) = showsPrec n x--instance (Arbitrary a, ShrinkState s a) => Arbitrary (Shrinking s a) where- arbitrary =- do x <- arbitrary- return (Shrinking (shrinkInit x) x)-- shrink (Shrinking s x) =- [ Shrinking s' x'- | (x',s') <- shrinkState x s- ]--#endif /* NO_MULTI_PARAM_TYPE_CLASSES */------------------------------------------------------------------------------- the end.
− Test/QuickCheck/Monadic.hs
@@ -1,109 +0,0 @@-{-# LANGUAGE CPP #-}-#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------------------------------------------------------------------------------- imports--import Test.QuickCheck.Gen-import Test.QuickCheck.Gen.Unsafe-import Test.QuickCheck.Property--import Control.Monad(liftM, liftM2)--import Control.Monad.ST-import Control.Applicative--#ifndef NO_TRANSFORMERS-import Control.Monad.IO.Class-import Control.Monad.Trans.Class-#endif------------------------------------------------------------------------------- type PropertyM--newtype PropertyM m a =- MkPropertyM { unPropertyM :: (a -> Gen (m Property)) -> Gen (m Property) }--instance Functor (PropertyM m) where- fmap f (MkPropertyM m) = MkPropertyM (\k -> m (k . f))--instance Monad m => Applicative (PropertyM m) where- pure = return- (<*>) = liftM2 ($)--instance Monad m => Monad (PropertyM m) where- 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)))---- should think about strictness/exceptions here---assert :: Testable prop => prop -> PropertyM m ()-assert :: Monad m => Bool -> PropertyM m ()-assert True = return ()-assert False = fail "Assertion failed"---- should think about strictness/exceptions here-pre :: Monad m => Bool -> PropertyM m ()-pre True = return ()-pre False = stop rejected---- should be called lift?-run :: Monad m => m a -> PropertyM m a-run m = MkPropertyM (liftM (m >>=) . promote)--pick :: (Monad m, Show a) => Gen a -> PropertyM m a-pick gen = MkPropertyM $ \k ->- do a <- gen- mp <- k a- return (do p <- mp- return (forAll (return a) (const p)))--wp :: Monad m => m a -> (a -> PropertyM m b) -> PropertyM m b-wp m k = run m >>= k--forAllM :: (Monad m, Show a) => Gen a -> (a -> PropertyM m b) -> PropertyM m b-forAllM gen k = pick gen >>= k--monitor :: Monad m => (Property -> Property) -> PropertyM m ()-monitor f = MkPropertyM (\k -> (f `liftM`) `fmap` (k ()))---- run functions--monadic :: Monad m => (m Property -> Property) -> PropertyM m a -> Property-monadic runner m = property (fmap runner (monadic' m))--monadic' :: Monad m => PropertyM m a -> Gen (m Property)-monadic' (MkPropertyM m) = m (const (return (return (property True))))--monadicIO :: PropertyM IO a -> Property-monadicIO = monadic ioProperty--#ifndef NO_ST_MONAD-monadicST :: (forall s. PropertyM (ST s) a) -> Property-monadicST m = property (runSTGen (monadic' m))--runSTGen :: (forall s. Gen (ST s a)) -> Gen a-runSTGen f = do- Capture eval <- capture- return (runST (eval f))-#endif------------------------------------------------------------------------------- the end.
− Test/QuickCheck/Poly.hs
@@ -1,133 +0,0 @@-{-# LANGUAGE CPP #-}-#ifndef NO_NEWTYPE_DERIVING-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-#endif--- | Types to help with testing polymorphic properties.------ Types 'A', 'B' and 'C' are @newtype@ wrappers around 'Integer' that--- implement 'Eq', 'Show', 'Arbitrary' and 'CoArbitrary'. Types--- 'OrdA', 'OrdB' and 'OrdC' also implement 'Ord' and 'Num'.------ See also "Test.QuickCheck.All" for an automatic way of testing--- polymorphic properties.-module Test.QuickCheck.Poly- ( A(..), B(..), C(..)- , OrdA(..), OrdB(..), OrdC(..)- )- where------------------------------------------------------------------------------- imports--import Test.QuickCheck.Arbitrary------------------------------------------------------------------------------- polymorphic A, B, C (in Eq)---- A--newtype A = A{ unA :: Integer }- deriving ( Eq )--instance Show A where- showsPrec n (A x) = showsPrec n x--instance Arbitrary A where- arbitrary = (A . (+1) . abs) `fmap` arbitrary- shrink (A x) = [ A x' | x' <- shrink x, x' > 0 ]--instance CoArbitrary A where- coarbitrary = coarbitrary . unA---- B--newtype B = B{ unB :: Integer }- deriving ( Eq )--instance Show B where- showsPrec n (B x) = showsPrec n x--instance Arbitrary B where- arbitrary = (B . (+1) . abs) `fmap` arbitrary- shrink (B x) = [ B x' | x' <- shrink x, x' > 0 ]--instance CoArbitrary B where- coarbitrary = coarbitrary . unB---- C--newtype C = C{ unC :: Integer }- deriving ( Eq )--instance Show C where- showsPrec n (C x) = showsPrec n x--instance Arbitrary C where- arbitrary = (C . (+1) . abs) `fmap` arbitrary- shrink (C x) = [ C x' | x' <- shrink x, x' > 0 ]--instance CoArbitrary C where- coarbitrary = coarbitrary . unC------------------------------------------------------------------------------- polymorphic OrdA, OrdB, OrdC (in Eq, Ord)---- OrdA--newtype OrdA = OrdA{ unOrdA :: Integer }- deriving ( Eq, Ord-#ifndef NO_NEWTYPE_DERIVING- , Num-#endif- )--instance Show OrdA where- showsPrec n (OrdA x) = showsPrec n x--instance Arbitrary OrdA where- arbitrary = (OrdA . (+1) . abs) `fmap` arbitrary- shrink (OrdA x) = [ OrdA x' | x' <- shrink x, x' > 0 ]--instance CoArbitrary OrdA where- coarbitrary = coarbitrary . unOrdA---- OrdB--newtype OrdB = OrdB{ unOrdB :: Integer }- deriving ( Eq, Ord-#ifndef NO_NEWTYPE_DERIVING- , Num-#endif- )--instance Show OrdB where- showsPrec n (OrdB x) = showsPrec n x--instance Arbitrary OrdB where- arbitrary = (OrdB . (+1) . abs) `fmap` arbitrary- shrink (OrdB x) = [ OrdB x' | x' <- shrink x, x' > 0 ]--instance CoArbitrary OrdB where- coarbitrary = coarbitrary . unOrdB---- OrdC--newtype OrdC = OrdC{ unOrdC :: Integer }- deriving ( Eq, Ord-#ifndef NO_NEWTYPE_DERIVING- , Num-#endif- )--instance Show OrdC where- showsPrec n (OrdC x) = showsPrec n x--instance Arbitrary OrdC where- arbitrary = (OrdC . (+1) . abs) `fmap` arbitrary- shrink (OrdC x) = [ OrdC x' | x' <- shrink x, x' > 0 ]--instance CoArbitrary OrdC where- coarbitrary = coarbitrary . unOrdC------------------------------------------------------------------------------- the end.
− Test/QuickCheck/Property.hs
@@ -1,517 +0,0 @@--- | Combinators for constructing properties.-{-# LANGUAGE CPP #-}-#ifndef NO_SAFE_HASKELL-{-# LANGUAGE Safe #-}-#endif-module Test.QuickCheck.Property where------------------------------------------------------------------------------- imports--import Test.QuickCheck.Gen-import Test.QuickCheck.Gen.Unsafe-import Test.QuickCheck.Arbitrary-import Test.QuickCheck.Text( showErr, isOneLine, putLine )-import Test.QuickCheck.Exception-import Test.QuickCheck.State--#ifndef NO_TIMEOUT-import System.Timeout(timeout)-#endif-import Data.Maybe-import Control.Applicative-import Control.Monad------------------------------------------------------------------------------- fixities--infixr 0 ==>-infixr 1 .&.-infixr 1 .&&.-infixr 1 .||.---- The story for exception handling:------ To avoid insanity, we have rules about which terms can throw--- exceptions when we evaluate them:--- * A rose tree must evaluate to WHNF without throwing an exception--- * The 'ok' component of a Result must evaluate to Just True or--- Just False or Nothing rather than raise an exception--- * IORose _ must never throw an exception when executed------ Both rose trees and Results may loop when we evaluate them, though,--- so we have to be careful not to force them unnecessarily.------ We also have to be careful when we use fmap or >>= in the Rose--- monad that the function we supply is total, or else use--- protectResults afterwards to install exception handlers. The--- mapResult function on Properties installs an exception handler for--- us, though.------ Of course, the user is free to write "error "ha ha" :: Result" if--- they feel like it. We have to make sure that any user-supplied Rose--- Results or Results get wrapped in exception handlers, which we do by:--- * Making the 'property' function install an exception handler--- round its argument. This function always gets called in the--- right places, because all our Property-accepting functions are--- actually polymorphic over the Testable class so they have to--- call 'property'.--- * Installing an exception handler round a Result before we put it--- in a rose tree (the only place Results can end up).------------------------------------------------------------------------------- * 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.-class Testable prop where- -- | Convert the thing to a property.- property :: prop -> Property- -- | If true, the property will only be tested once.- -- However, if used inside a quantifier, it will be tested normally.- exhaustive :: prop -> Bool- exhaustive _ = False---- | If a property returns 'Discard', the current test case is discarded,--- the same as if a precondition was false.-data Discard = Discard--instance Testable Discard where- property _ = property rejected- exhaustive _ = True--instance Testable Bool where- property = property . liftBool- exhaustive _ = True--instance Testable Result where- property = MkProperty . return . MkProp . protectResults . return- exhaustive _ = True--instance Testable Prop where- property (MkProp r) = MkProperty . return . MkProp . ioRose . return $ r- exhaustive _ = True--instance Testable prop => Testable (Gen prop) where- property mp = MkProperty $ do p <- mp; unProperty (property p)--instance Testable Property where- property = property . unProperty---- | Do I/O inside a property. This can obviously lead to unrepeatable--- testcases, so use with care.-{-# DEPRECATED morallyDubiousIOProperty "Use ioProperty instead" #-}-morallyDubiousIOProperty :: Testable prop => IO prop -> Property-morallyDubiousIOProperty = ioProperty -- Silly names aren't all they're cracked up to be :)---- | Do I/O inside a property. This can obviously lead to unrepeatable--- testcases, so use with care.------ For more advanced monadic testing you may want to look at--- "Test.QuickCheck.Monadic".-ioProperty :: Testable prop => IO prop -> Property-ioProperty = MkProperty . fmap (MkProp . ioRose . fmap unProp) . promote . fmap (unProperty . property)--instance (Arbitrary a, Show a, Testable prop) => Testable (a -> prop) where- property f = forAllShrink arbitrary shrink f---- ** Exception handling-protect :: (AnException -> a) -> IO a -> IO a-protect f x = either f id `fmap` tryEvaluateIO x------------------------------------------------------------------------------- ** Type Prop--newtype Prop = MkProp{ unProp :: Rose Result }---- ** type Rose--data Rose a = MkRose a [Rose a] | IORose (IO (Rose a))--- Only use IORose if you know that the argument is not going to throw an exception!--- Otherwise, try ioRose.-ioRose :: IO (Rose Result) -> Rose Result-ioRose = IORose . protectRose--joinRose :: Rose (Rose a) -> Rose a-joinRose (IORose rs) = IORose (fmap joinRose rs)-joinRose (MkRose (IORose rm) rs) = IORose $ do r <- rm; return (joinRose (MkRose r rs))-joinRose (MkRose (MkRose x ts) tts) =- -- first shrinks outer quantification; makes most sense- MkRose x (map joinRose tts ++ ts)- -- first shrinks inner quantification: terrible- --MkRose x (ts ++ map joinRose tts)--instance Functor Rose where- -- f must be total- fmap f (IORose rs) = IORose (fmap (fmap f) rs)- fmap f (MkRose x rs) = MkRose (f x) [ fmap f r | r <- rs ]--instance Applicative Rose where- pure = return- -- f must be total- (<*>) = liftM2 ($)--instance Monad Rose where- return x = MkRose x []- -- k must be total- m >>= k = joinRose (fmap k m)---- 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.--- 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.-protectRose :: IO (Rose Result) -> IO (Rose Result)-protectRose = protect (return . exception "Exception")---- Wrap all the Results in a rose tree in exception handlers.-protectResults :: Rose Result -> Rose Result-protectResults = onRose $ \x rs ->- IORose $ do- y <- protectResult (return x)- return (MkRose y (map protectResults rs))---- ** Result type---- | Different kinds of callbacks-data Callback- = PostTest CallbackKind (State -> Result -> IO ()) -- ^ Called just after a test- | PostFinalFailure CallbackKind (State -> Result -> IO ()) -- ^ Called with the final failing test-case-data CallbackKind = Counterexample -- ^ Affected by the 'verbose' combinator- | NotCounterexample -- ^ Not affected by the 'verbose' combinator---- | The result of a single test.-data Result- = MkResult- { ok :: Maybe Bool -- ^ result of the test case; Nothing = discard- , expect :: Bool -- ^ indicates what the expected result of the property is- , 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- , callbacks :: [Callback] -- ^ the callbacks for this test case- }--result :: Result-result =- MkResult- { ok = undefined- , expect = True- , reason = ""- , theException = Nothing- , abort = False- , stamp = []- , callbacks = []- }--exception :: String -> AnException -> Result-exception msg err- | isDiscard err = rejected- | otherwise = failed{ reason = formatException msg err,- theException = Just err }--formatException :: String -> AnException -> String-formatException msg err = msg ++ ":" ++ format (show err)- where format xs | isOneLine xs = " '" ++ xs ++ "'"- | otherwise = "\n" ++ unlines [ " " ++ l | l <- lines xs ]--protectResult :: IO Result -> IO Result-protectResult = protect (exception "Exception")--succeeded :: Result-succeeded = result{ ok = Just True }--failed :: Result-failed = result{ ok = Just False }--rejected :: Result-rejected = result{ ok = Nothing }------------------------------------------------------------------------------- ** Lifting and mapping functions--liftBool :: Bool -> Result-liftBool True = succeeded-liftBool False = failed { reason = "Falsifiable" }--mapResult :: Testable prop => (Result -> Result) -> prop -> Property-mapResult f = mapRoseResult (protectResults . fmap f)--mapTotalResult :: Testable prop => (Result -> Result) -> prop -> Property-mapTotalResult f = mapRoseResult (fmap f)---- f here mustn't throw an exception (rose tree invariant).-mapRoseResult :: Testable prop => (Rose Result -> Rose Result) -> prop -> Property-mapRoseResult f = mapProp (\(MkProp t) -> MkProp (f t))--mapProp :: Testable prop => (Prop -> Prop) -> prop -> Property-mapProp f = MkProperty . fmap f . unProperty . property------------------------------------------------------------------------------- ** Property combinators---- | Changes the maximum test case size for a property.-mapSize :: Testable prop => (Int -> Int) -> prop -> Property-mapSize f p = MkProperty (sized ((`resize` unProperty (property p)) . f))---- | Shrinks the argument to property if it fails. Shrinking is done--- automatically for most types. This is only needed when you want to--- override the default behavior.-shrinking :: Testable prop =>- (a -> [a]) -- ^ 'shrink'-like function.- -> a -- ^ The original argument- -> (a -> prop) -> Property-shrinking shrinker x0 pf = MkProperty (fmap (MkProp . joinRose . fmap unProp) (promote (props x0)))- where- props x =- MkRose (unProperty (property (pf x))) [ props x' | x' <- shrinker x ]---- | Disables shrinking for a property altogether.-noShrinking :: Testable prop => prop -> Property-noShrinking = mapRoseResult (onRose (\res _ -> MkRose res []))---- | Adds a callback-callback :: Testable prop => Callback -> prop -> Property-callback cb = mapTotalResult (\res -> res{ callbacks = cb : callbacks res })---- | Adds the given string to the counterexample.-counterexample :: Testable prop => String -> prop -> Property-counterexample s =- callback $ PostFinalFailure Counterexample $ \st _res -> do- res <- tryEvaluateIO (putLine (terminal st) s)- case res of- Left err ->- putLine (terminal st) (formatException "Exception thrown by generator" err)- Right () ->- return ()---- | Adds the given string to the counterexample.-{-# DEPRECATED printTestCase "Use counterexample instead" #-}-printTestCase :: Testable prop => String -> prop -> Property-printTestCase = counterexample---- | Performs an 'IO' action after the last failure of a property.-whenFail :: Testable prop => IO () -> prop -> Property-whenFail m =- callback $ PostFinalFailure NotCounterexample $ \_st _res ->- m---- | Performs an 'IO' action every time a property fails. Thus,--- if shrinking is done, this can be used to keep track of the--- failures along the way.-whenFail' :: Testable prop => IO () -> prop -> Property-whenFail' m =- callback $ PostTest NotCounterexample $ \_st res ->- if ok res == Just False- then m- else return ()---- | Prints out the generated testcase every time the property is tested.--- Only variables quantified over /inside/ the 'verbose' are printed.-verbose :: Testable prop => prop -> Property-verbose = mapResult (\res -> res { callbacks = newCallbacks (callbacks res) ++ callbacks res })- where newCallbacks cbs =- PostTest Counterexample (\st res -> putLine (terminal st) (status res ++ ":")):- [ PostTest Counterexample f | PostFinalFailure Counterexample f <- cbs ]- status MkResult{ok = Just True} = "Passed"- status MkResult{ok = Just False} = "Failed"- status MkResult{ok = Nothing} = "Skipped (precondition false)"---- | Indicates that a property is supposed to fail.--- QuickCheck will report an error if it does not fail.-expectFailure :: Testable prop => prop -> Property-expectFailure = mapTotalResult (\res -> res{ expect = False })---- | Modifies a property so that it only will be tested once.-once :: Testable prop => prop -> Property-once = mapTotalResult (\res -> res{ abort = True })---- | Attaches a label to a property. This is used for reporting--- test case distribution.-label :: Testable prop => String -> prop -> Property-label s = classify True s---- | Labels a property with a value:------ > collect x = label (show x)-collect :: (Show a, Testable prop) => a -> prop -> Property-collect x = label (show x)---- | Conditionally labels test case.-classify :: Testable prop =>- Bool -- ^ @True@ if the test case should be labelled.- -> String -- ^ Label.- -> 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.-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 })- 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),--- or if the given property holds.-(==>) :: Testable prop => Bool -> prop -> Property-False ==> _ = property Discard-True ==> p = property p---- | Considers a property failed if it does not complete within--- the given number of microseconds.-within :: Testable prop => Int -> prop -> Property-within n = mapRoseResult f- -- We rely on the fact that the property will catch the timeout- -- exception and turn it into a failed test case.- where- f rose = ioRose $ do- let m `orError` x = fmap (fromMaybe (error x)) m- MkRose res roses <- timeout n (reduceRose rose) `orError`- "within: timeout exception not caught in Rose Result"- res' <- timeout n (protectResult (return res)) `orError`- "within: timeout exception not caught in Result"- return (MkRose res' (map f roses))-#ifdef NO_TIMEOUT- timeout _ = fmap Just-#endif---- | Explicit universal quantification: uses an explicitly given--- test case generator.-forAll :: (Show a, Testable prop)- => Gen a -> (a -> prop) -> Property-forAll gen pf =- MkProperty $- gen >>= \x ->- unProperty (counterexample (show x) (pf x))---- | Like 'forAll', but tries to shrink the argument for failing test cases.-forAllShrink :: (Show a, Testable prop)- => Gen a -> (a -> [a]) -> (a -> prop) -> Property-forAllShrink gen shrinker pf =- MkProperty $- gen >>= \x ->- unProperty $- shrinking shrinker x $ \x' ->- counterexample (show x') (pf x')---- | Nondeterministic choice: 'p1' '.&.' 'p2' picks randomly one of--- 'p1' and 'p2' to test. If you test the property 100 times it--- makes 100 random choices.-(.&.) :: (Testable prop1, Testable prop2) => prop1 -> prop2 -> Property-p1 .&. p2 =- MkProperty $- arbitrary >>= \b ->- unProperty $- counterexample (if b then "LHS" else "RHS") $- if b then property p1 else property p2---- | Conjunction: 'p1' '.&&.' 'p2' passes if both 'p1' and 'p2' pass.-(.&&.) :: (Testable prop1, Testable prop2) => prop1 -> prop2 -> Property-p1 .&&. p2 = conjoin [property p1, property p2]---- | Take the conjunction of several properties.-conjoin :: Testable prop => [prop] -> Property-conjoin ps =- MkProperty $- do roses <- mapM (fmap unProp . unProperty . property) ps- return (MkProp (conj [] roses))- where- conj cbs [] =- MkRose succeeded{callbacks = cbs} []-- conj cbs (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 False -> return rose- Nothing -> do- rose2@(MkRose result2 _) <- reduceRose (conj (cbs ++ callbacks result) ps)- return $- -- Nasty work to make sure we use the right callbacks- case ok result2 of- Just True -> MkRose (result2 { ok = Nothing }) []- Just False -> rose2- Nothing -> rose2---- | Disjunction: 'p1' '.||.' 'p2' passes unless 'p1' and 'p2' simultaneously fail.-(.||.) :: (Testable prop1, Testable prop2) => prop1 -> prop2 -> Property-p1 .||. p2 = disjoin [property p1, property p2]---- | Take the disjunction of several properties.-disjoin :: Testable prop => [prop] -> Property-disjoin ps =- MkProperty $- do roses <- mapM (fmap unProp . unProperty . property) ps- return (MkProp (foldr disj (MkRose failed []) roses))- where- disj :: Rose Result -> Rose Result -> Rose Result- disj p q =- do result1 <- p- case ok result1 of- _ | not (expect result1) -> return expectFailureError- Just True -> return result1- 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- Nothing -> do- result2 <- q- return (case ok result2 of- _ | not (expect result2) -> expectFailureError- Just True -> result2- _ -> 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- }---- | Like '==', but prints a counterexample when it fails.-infix 4 ===-(===) :: (Eq a, Show a) => a -> a -> Property-x === y =- counterexample (show x ++ " /= " ++ show y) (x == y)-------------------------------------------------------------------------------- the end.
− Test/QuickCheck/Random.hs
@@ -1,108 +0,0 @@--- | A wrapper around the system random number generator. Internal QuickCheck module.-{-# LANGUAGE CPP #-}-#ifndef NO_SAFE_HASKELL-{-# LANGUAGE Trustworthy #-}-#endif-module Test.QuickCheck.Random where--#ifndef NO_TF_RANDOM-import System.Random-import System.Random.TF-import System.Random.TF.Gen(splitn)-import Data.Word-import Data.Bits--#define TheGen TFGen--newTheGen :: IO TFGen-newTheGen = newTFGen--bits, mask, doneBit :: Integral a => a-bits = 14-mask = 0x3fff-doneBit = 0x4000--chip :: Bool -> Word32 -> TFGen -> TFGen-chip done n g = splitn g (bits+1) (if done then m .|. doneBit else m)- where- m = n .&. mask--chop :: Integer -> Integer-chop n = n `shiftR` bits--stop :: Integral a => a -> Bool-stop n = n <= mask--mkTheGen :: Int -> TFGen-mkTheGen = mkTFGen--#else-import System.Random--#define TheGen StdGen--newTheGen :: IO StdGen-newTheGen = newStdGen--mkTheGen :: Int -> StdGen-mkTheGen = mkStdGen--chip :: Bool -> Int -> StdGen -> StdGen-chip finished n = boolVariant finished . boolVariant (even n)--chop :: Integer -> Integer-chop n = n `div` 2--stop :: Integral a => a -> Bool-stop n = n <= 1-#endif---- | The "standard" QuickCheck random number generator.--- A wrapper around either 'TFGen' on GHC, or 'StdGen'--- on other Haskell systems.-newtype QCGen = QCGen TheGen--instance Show QCGen where- showsPrec n (QCGen g) = showsPrec n g-instance Read QCGen where- readsPrec n xs = [(QCGen g, ys) | (g, ys) <- readsPrec n xs]--instance RandomGen QCGen where- split (QCGen g) = (QCGen g1, QCGen g2)- where- (g1, g2) = split g- genRange (QCGen g) = genRange g- next (QCGen g) = (x, QCGen g')- where- (x, g') = next g--newQCGen :: IO QCGen-newQCGen = fmap QCGen newTheGen--mkQCGen :: Int -> QCGen-mkQCGen n = QCGen (mkTheGen n)--bigNatVariant :: Integer -> TheGen -> TheGen-bigNatVariant n g- | g `seq` stop n = chip True (fromInteger n) g- | otherwise = (bigNatVariant $! chop n) $! chip False (fromInteger n) g--{-# INLINE natVariant #-}-natVariant :: Integral a => a -> TheGen -> TheGen-natVariant n g- | g `seq` stop n = chip True (fromIntegral n) g- | otherwise = bigNatVariant (toInteger n) g--{-# INLINE variantTheGen #-}-variantTheGen :: Integral a => a -> TheGen -> TheGen-variantTheGen n g- | n >= 1 = natVariant (n-1) (boolVariant False g)- | n == 0 = natVariant (0 `asTypeOf` n) (boolVariant True g)- | otherwise = bigNatVariant (negate (toInteger n)) (boolVariant True g)--boolVariant :: Bool -> TheGen -> TheGen-boolVariant False = fst . split-boolVariant True = snd . split--variantQCGen :: Integral a => a -> QCGen -> QCGen-variantQCGen n (QCGen g) = QCGen (variantTheGen n g)
− Test/QuickCheck/State.hs
@@ -1,36 +0,0 @@--- | QuickCheck's internal state. Internal QuickCheck module.-module Test.QuickCheck.State where--import Test.QuickCheck.Text-import Test.QuickCheck.Random------------------------------------------------------------------------------- State---- | State represents QuickCheck's internal state while testing a property.--- The state is made visible to callback functions.-data State- = MkState- -- static- { terminal :: Terminal -- ^ the current terminal- , 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-- -- 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-- -- 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- }------------------------------------------------------------------------------- the end.
− Test/QuickCheck/Test.hs
@@ -1,421 +0,0 @@--- | The main test loop.-{-# LANGUAGE CPP #-}-#ifndef NO_SAFE_HASKELL-{-# LANGUAGE Safe #-}-#endif-module Test.QuickCheck.Test where------------------------------------------------------------------------------- imports--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---- * Running tests---- | Args specifies arguments to the QuickCheck driver-data Args- = Args- { replay :: Maybe (QCGen,Int) -- ^ Should we replay a previous test?- , maxSuccess :: Int -- ^ Maximum number of successful tests before succeeding- , maxDiscardRatio :: Int -- ^ Maximum number of discarded tests per successful test before giving up- , maxSize :: Int -- ^ Size to use for the biggest test cases- , chatty :: Bool -- ^ Whether to print anything- }- deriving ( Show, Read )---- | Result represents the test result-data Result- -- | A successful test run- = Success- { numTests :: Int -- ^ Number of tests performed- , labels :: [(String,Int)] -- ^ Labels and frequencies found during all successful tests- , output :: String -- ^ Printed output- }- -- | Given up- | GaveUp- { numTests :: Int -- Number of tests performed- , labels :: [(String,Int)] -- Labels and frequencies found during all successful tests- , output :: String -- Printed output- }- -- | A failed test run- | Failure- { numTests :: Int -- Number of tests performed- , numShrinks :: Int -- ^ Number of successful shrinking steps performed- , numShrinkTries :: Int -- ^ Number of unsuccessful shrinking steps performed- , numShrinkFinal :: Int -- ^ Number of unsuccessful shrinking steps performed since last successful shrink- , usedSeed :: QCGen -- ^ What seed was used- , usedSize :: Int -- ^ What was the test size- , reason :: String -- ^ Why did the property fail- , theException :: Maybe AnException -- ^ The exception the property threw, if any- , labels :: [(String,Int)] -- Labels and frequencies found during all successful tests- , output :: String -- Printed output- }- -- | A property that should have failed did not- | NoExpectedFailure- { 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-isSuccess :: Result -> Bool-isSuccess Success{} = True-isSuccess _ = False---- | The default test arguments-stdArgs :: Args-stdArgs = Args- { replay = Nothing- , maxSuccess = 100- , maxDiscardRatio = 10- , maxSize = 100- , chatty = True--- noShrinking flag?- }---- | Tests a property and prints the results to 'stdout'.-quickCheck :: Testable prop => prop -> IO ()-quickCheck p = quickCheckWith stdArgs p---- | Tests a property, using test arguments, and prints the results to 'stdout'.-quickCheckWith :: Testable prop => Args -> prop -> IO ()-quickCheckWith args p = quickCheckWithResult args p >> return ()---- | Tests a property, produces a test result, and prints the results to 'stdout'.-quickCheckResult :: Testable prop => prop -> IO Result-quickCheckResult p = quickCheckWithResult stdArgs p---- | Tests a property, using test arguments, produces a test result, and prints the results to 'stdout'.-quickCheckWithResult :: Testable prop => Args -> prop -> IO Result-quickCheckWithResult 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- } (unGen (unProperty (property' p)))- 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- property' p- | exhaustive p = once (property p)- | otherwise = property p---- | Tests a property and prints the results and all test cases generated to 'stdout'.--- This is just a convenience function that means the same as @'quickCheck' . 'verbose'@.-verboseCheck :: Testable prop => prop -> IO ()-verboseCheck p = quickCheck (verbose p)---- | Tests a property, using test arguments, and prints the results and all test cases generated to 'stdout'.--- This is just a convenience function that combines 'quickCheckWith' and 'verbose'.-verboseCheckWith :: Testable prop => Args -> prop -> IO ()-verboseCheckWith args p = quickCheckWith args (verbose p)---- | Tests a property, produces a test result, and prints the results and all test cases generated to 'stdout'.--- This is just a convenience function that combines 'quickCheckResult' and 'verbose'.-verboseCheckResult :: Testable prop => prop -> IO Result-verboseCheckResult p = quickCheckResult (verbose p)---- | Tests a property, using test arguments, produces a test result, and prints the results and all test cases generated to 'stdout'.--- This is just a convenience function that combines 'quickCheckWithResult' and 'verbose'.-verboseCheckWithResult :: Testable prop => Args -> prop -> IO Result-verboseCheckWithResult a p = quickCheckWithResult a (verbose p)------------------------------------------------------------------------------- main test loop--test :: State -> (QCGen -> Int -> Prop) -> IO 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 -> 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 }--giveUp :: State -> (QCGen -> Int -> Prop) -> IO 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 GaveUp{ numTests = numSuccessTests st- , labels = summary st- , output = theOutput- }--runATest :: State -> (QCGen -> Int -> Prop) -> IO 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)- MkRose res ts <- protectRose (reduceRose (unProp (f rnd1 size)))- callbackPostTest st res-- let continue break st' | abort res = break st'- | otherwise = test st'-- case res of- MkResult{ok = Just True, stamp = stamp, expect = 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- return Success{ labels = summary st,- numTests = numSuccessTests st+1,- output = theOutput }- else- return Failure{ usedSeed = randomSeed st -- correct! (this will be split first)- , 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.
− Test/QuickCheck/Text.hs
@@ -1,168 +0,0 @@--- | Terminal control. Internal QuickCheck module.-module Test.QuickCheck.Text- ( Str(..)- , ranges-- , number- , short- , showErr- , oneLine- , isOneLine- , bold-- , newTerminal- , withStdioTerminal- , withNullTerminal- , terminalOutput- , handle- , Terminal- , putTemp- , putPart- , putLine- )- where------------------------------------------------------------------------------- imports--import Control.Applicative-import System.IO- ( hFlush- , hPutStr- , stdout- , stderr- , Handle- , BufferMode (..)- , hGetBuffering- , hSetBuffering- )--import Data.IORef-import Test.QuickCheck.Exception------------------------------------------------------------------------------- literal string--newtype Str = MkStr String--instance Show Str where- show (MkStr s) = s--ranges :: (Show a, Integral a) => a -> a -> Str-ranges k n = MkStr (show n' ++ " -- " ++ show (n'+k-1))- where- n' = k * (n `div` k)------------------------------------------------------------------------------- formatting--number :: Int -> String -> String-number n s = show n ++ " " ++ s ++ if n == 1 then "" else "s"--short :: Int -> String -> String-short n s- | n < k = take (n-2-i) s ++ ".." ++ drop (k-i) s- | otherwise = s- where- k = length s- i = if n >= 5 then 3 else 0--showErr :: Show a => a -> String-showErr = unwords . words . show--oneLine :: String -> String-oneLine = unwords . words--isOneLine :: String -> Bool-isOneLine xs = '\n' `notElem` xs--bold :: String -> String--- not portable:---bold s = "\ESC[1m" ++ s ++ "\ESC[0m"-bold s = s -- for now------------------------------------------------------------------------------- putting strings--data Terminal- = MkTerminal (IORef (IO ())) Output Output--data Output- = Output (String -> IO ()) (IORef String)--newTerminal :: Output -> Output -> IO Terminal-newTerminal out err =- do ref <- newIORef (return ())- return (MkTerminal ref out err)--withBuffering :: IO a -> IO a-withBuffering action = do- mode <- hGetBuffering stderr- -- By default stderr is unbuffered. This is very slow, hence we explicitly- -- enable line buffering.- hSetBuffering stderr LineBuffering- 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)--withNullTerminal :: (Terminal -> IO a) -> IO a-withNullTerminal action = do- out <- output (const (return ()))- err <- output (const (return ()))- newTerminal out err >>= action--terminalOutput :: Terminal -> IO String-terminalOutput (MkTerminal _ out _) = get out--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')--putPart, putTemp, putLine :: Terminal -> String -> IO ()-putPart tm@(MkTerminal _ out _) s =- do flush tm- put out s--putTemp tm@(MkTerminal _ _ err) s =- do flush tm- put err (s ++ [ '\b' | _ <- s ])- postpone tm $- put err ( [ ' ' | _ <- s ]- ++ [ '\b' | _ <- s ]- )--putLine tm@(MkTerminal _ out _) s =- do flush tm- put out (s ++ "\n")------------------------------------------------------------------------------- the end.
− changelog
@@ -1,73 +0,0 @@-QuickCheck 2.7.3 (released 2014-03-24)- * Add annotations for Safe Haskell.--QuickCheck 2.7.2 (released 2014-03-22)- * Fix bug in cabal file which broke cabal test--QuickCheck 2.7.1 (released 2014-03-20)- * Fixed bug - the Small modifier didn't work on unsigned types- * Changed arbitrarySizedIntegral to have an Integral constraint- instead of just Num--QuickCheck 2.7 (released 2014-03-19)-- * New features:- * New genericShrink function provides generic shrinking with GHC.- * New combinator x === y: fails if x /= y, but also prints their values- * New function generate :: Gen a -> IO a for running a generator.- * New combinators infiniteList and infiniteListOf for generating infinite lists.- * Several combinators added to the main Test.QuickCheck module which- were previously languishing in other modules. Of particular interest:- quickCheckAll, ioProperty.- * New combinators delay and capture which can be used (unsafely!)- to reuse the random number seed. Useful for generating- polymorphic (rank-2) values.- * A new Discard data type and a Testable instance for discarding test cases.- * All modifiers now have Functor instances and accessor functions.- * Pressing ctrl-C during shrinking now shows the last failed- test case, rather than the current shrinking candidate.- * Experimental support for UHC. You will need the latest version of Cabal from git.-- * Better distribution of test data:- * The Int generator now only generates fairly small numbers.- * The new Small and Large modifiers control the distribution of integers- (Small generates small numbers, Large from the whole range).- * Floating-point numbers shrink better.-- * Improved random number generation:- * QuickCheck now uses TFGen rather than StdGen on newer versions- of GHC, because StdGen's random numbers aren't always random.- * 'variant' now uses a prefix code. This should prevent some- potential bananaskins with coarbitrary.-- * API changes:- * The Gen monad now uses an abstract type QCGen rather than StdGen.- * The Result type now returns the thrown exception and number- of failed shrink attempts.- * Property is now a newtype rather than Gen Prop as it was before.- * promote is moved into the new module Test.QuickCheck.Gen.Unsafe.- * 'printTestCase' is deprecated - its new name is 'counterexample'- * 'morallyDubiousIOProperty' is deprecated - its new name is- 'ioProperty', no moral judgement involved :)--QuickCheck 2.6, released 2013-03-07-- * Add convenience Function instances for up to 7-tuples- * Make stderr line buffered to reduce console I/O.- * Return a flag to say whether the test case was interrupted.--QuickCheck 2.5, released 2012-06-18-- * Replace maxDiscard with maxDiscardRatio- * Remove Testable () instance.- * Added a 'discard' exception that discards the current test case- * Add accessors for modifiers (where it makes sense)- * Rename 'stop' to 'abort' to avoid a name clash- * Added a 'once' combinator- * If a property is of type Bool, only run it once- * Add coarbitraryEnum to Test.QuickCheck module.- * Add 'coarbitrary' helper for Enums.- * Rejiggled the formatting code to support multi-line error messages- * Add instances for Ordering and Fixed.- * Added arbitraryBoundedEnum generator (thanks to Antoine Latter).- * Add verboseCheckAll and polyverboseCheck function for usability.
+ changelog.md view
@@ -0,0 +1,457 @@+## UNRELEASED++## QuickCheck 2.18.0.0 (released 2026-02-26)+* BREAKING: Added a number of `CoArbitrary` and `Function` instances for types in `base`+* Improve implementation of `shrinkIntegral` (thanks tom93)+* Improve implementation of `Arbitrary NonEmptyList` (thanks konsumlamm)+* Convert changelog to markdown (thanks Artem)+* Deprecate `withMaxSuccess` in favour of the renamed `withNumTests`+* `Test.QuickCheck.Function` marked as `Trustworthy` not `Safe`+* Fix bug in heap example++## QuickCheck 2.17.1.0 (released 2025-10-13)+* Fix random-1.2 support (thanks bodigrim)+* Update docs (thanks effectfully)++## QuickCheck 2.17.0.0 (released 2025-10-07)+* Lower the memory use of `arbitrary` for `Double` and `Float` (thanks+ChickenProp)+* BREAKING: Remove `Gen.Class`+* BREAKING: Add `Arbitrary` instances for most of the types in `base`+* Add microHS support (thanks Lennart)++## QuickCheck 2.16.0.0 (released 2025-06-17)+* Add a `Safe` declaration to `Test.QuickCheck.Monoids`+* Fix `Test.QuickCheck.Features` to not depend on the internal testing loop+* Add `withProgress` to install common callbacks for downstream tools+* Relax bounds on `random` dependency (thanks to LaurentRDC and+Andreas Abel)+* Fix some haddock issues (thanks to pgujjula)+* Use `displayException` instead of `show` (thanks Edsko)+* Added helper monoids `Every` and `Some` for predicates (thanks+Marcin)++## QuickCheck 2.15 (released 2024-04-22)+* BREAKING: properties without quantification are no longer implicitly+ wrapped in once+* Add witness property modifier and witnesses field in result on+compilers that support Typeable to allow conveniently exposing+counterexamples+* Add assertException, assertExceptionIO, assertDeepException, and+assertDeepExceptionIO (thanks to Alexey Kuleshevich)+* Add withMaxSize, withMaxShrinks, and withMaxDiscardRatio combinators+* Add recheck+* Add HasCallStack to partial top-level functions+* Updated some documentation+* Fix overflow bug when calculating discard ratio+* Fix error printing bug when shrinks are discarded+* Fix withMaxSuccess not working when checkCoverage is turned on+* Fix a bug whereby an unfortunately timed discard could unduly fail a+property running with checkCoverage+* Fix Arbitrary intance for Map breaking invariants from+Data.Map.Strict (thanks to Neil Mayhew)+* Fix non-covered classes not showing up in output as 0% covered+* Fix Negative's Arbitrary instance discarding an unnecessary number+of generated values.+* Fix promote doing an unnecessary seed split+* Fix a bug whereby maxSuccess and discardRatio would interact poorly+to produce too few size increases.+* Fix a couple of underflow bugs in generators for Small Natural and+Ratio Natural+* Fix a bug whereby the internal DISCARDED exception would show up+user-side in sample++## QuickCheck 2.14.3 (released 2023-05-31)+* Add shrinkBoundedEnum (thanks to Jonathan Knowles)+* Add discardAfter for discarding tests on timeout (thanks to Justus Sagemüller)+* Add assertWith for monadic testing (thanks to KtorZ)+* Add functionElements to Test.QuickCheck.Function (thanks to Oleg Grenrus)+* Add Arbitrary instance for Newline (thanks to Daniel Bramucci)+* Improve Arbitrary instances for Float and Double (thanks to Oleg Grenrus)+* Improve arbitrarySizedFractional (thanks to Bodigrim)+* Fix shrinkRealFrac and shrinkDecimal, which were broken+* Speed up printing of progress messages (thanks to Bodigrim)+* Add COMPLETE pragmas for Fn and family (thanks to ilkecan)+* Make templateHaskell flag manual (thanks to Oleg Grenrus)++## QuickCheck 2.14.2 (released 2020-11-14)+* Add Arbitrary instances for Tree (thanks to Oleg Grenrus)+* GHC 9.0 compatibility (thanks to Vilem-Benjamin Liepelt)++## QuickCheck 2.14.1 (released 2020-07-05)+* Compatibility with random >= 1.2.++## QuickCheck 2.14 (released 2020-03-28)+* QuickCheck is now much faster at generating test data!+ As a result, many properties can now be tested a lot faster;+ the examples distributed with QuickCheck run about twice as+ fast as before, for example. Of course, your mileage may vary.++ The reason for this is that there are now specialised versions+ of the 'choose' combinator for various types. These are:+ chooseInt, chooseInteger, chooseBoundedIntegral, and chooseEnum.+ These combinators are identical to 'choose' but much faster.+ All QuickCheck combinators, with the exception of 'choose'+ itself, use the new combinators behind the scenes.++ You should see a speedup without doing anything, but to get a+ further speedup, consider replacing any uses of 'choose' in your+ own generators with the new combinators.++ We are hoping that future releases of the 'random' library may+ speed up 'choose', in which case these combinators may no longer+ be needed.++ Thanks to Oleg Grenrus for suggesting to bypass 'choose' for+ random number generation, and providing the appropriate+ primitive in his 'splitmix' random number library.++* Smaller changes and bugfixes:+- RecursivelyShrink and GSubterms are exported from+ Test.QuickCheck.Test (thanks to Tom Mortiboy).+- Don't generate invalid unicode characters+ (thanks to Boris Stepanov).+- When a call to 'within' fails, include the duration of the+ timeout in the failure report (thanks to William Rusnack).+- In Gen, avoid splitting the seed in the implementation of+ >>, *> and <* (thanks to David Feuer).+- Fix a couple of bugs with shrinking of floating-point+ numbers.+- Export functionMapWith, functionEitherWith and+ functionPairWith from Test.QuickCheck.Function+ (thanks to Oleg Grenrus).+- Remove redundant RealFloat constraint from+ Arbitrary/CoArbitrary instances for Complex+ (thanks to Bodigrim).++## QuickCheck 2.13.2 (released 2019-06-30)+* Compatibility with GHC 8.8 (thanks to Bodigrim)+* Improve error message when 'frequency' is used with only zero weights+* Add 'functionVoid' combinator (thanks to Oleg Grenrus)+* Tighten bounds for random package (thanks to Oleg Grenrus)++## QuickCheck 2.13.1 (released 2019-03-29)+* A couple of bug fixes++## QuickCheck 2.13 (released 2019-03-26)+* Properties with multiple arguments now shrink better.+ Previously, the first argument was shrunk, then the second, and+ so on. Now, the arguments are shrunk as a whole, so shrink steps+ for different arguments can be interleaved.++* New features:+ - New modifiers Negative and NonPositive+ - A Testable instance for Maybe prop (where Nothing means 'discard+ the test case')+* Dependencies on C code removed:+ - Use splitmix instead of tf-random for random number generation+ - Remove dependency on 'erf' package+* Small changes:+ - Say 'Falsified' instead of 'Falsifiable' when a property fails+* Compatibility improvements:+ - Explicitly derive instance Typeable Args+ - Lower bound on deepseq+ - A script for building Hugs packages++## QuickCheck 2.12.6 (released 2018-10-02)+* Make arbitrarySizedBoundedIntegral handle huge sizes correctly.+* Add changelog for QuickCheck 2.12.5 :)++## QuickCheck 2.12.5 (released 2018-09-30)+* Export isSuccess from Test.QuickCheck.+* Export CoArbitrary even when generics are disabled (bugfix).+* Fix bug in shrinkDecimal.+* Include Test.QuickCheck.Gen in exposed modules for Haddock.++## QuickCheck 2.12.3, 2.12.4 (released 2018-09-12)+* Shrinking for Float and Decimal now works by reducing the number+ of digits in the number. The new function shrinkDecimal+ implements this shrinking behaviour.+* Shrinking for Rational now tries to make the numerator and+ denominator of the number smaller. Previously it tried to reduce+ the magnitude of the number.++## QuickCheck 2.12.2 (released 2018-09-10)+* Fix infinite shrinking loop for fractional types.+* Add SortedList modifier.++## QuickCheck 2.12.1 (released 2018-09-06)+* Fix bug in 'classify'.++## QuickCheck 2.12 (released 2018-09-03)+* Silently breaking changes!+ - The Arbitrary instance for Word now generates only small+ values, the same as Int+ - cover no longer causes a property failure if coverage is+ insufficient. It just prints a warning. (But see next item!)++* Overhaul of label/cover family of combinators:+ - New property combinator checkCoverage, which checks coverage+ requirements in a statistically sound way, and *does* fail if+ they are not met.+ - Order of arguments to cover swapped, to make it easier to+ switch between classify and cover+ - New combinators tabulate and coverTable, for reporting test+ case distribution more flexibly than label.+ - When label is called multiple times in a property, each call+ produces a separate table of frequencies.++* New functions:+ - (=/=): like (/=), but prints a counterexample+ (thanks to tom-bop)+ - forAllShow/forAllShrinkShow: quantification using an+ explicit show function (thanks to Stevan Andjelkovic)+ - forAllBlind/forAllShrinkBlind: quantification without+ printing anything+ - verboseShrinking: see how a counterexample is shrunk+ - labelledExamples: given a property which uses label,+ generate an example test case for each label+ - idempotentIOProperty: a variant of ioProperty which shrinks+ better but only works for idempotent I/O actions++* Other improvements:+ - MonadFix Gen instance (thanks to Jon Fowler)+ - Rational numbers shrink using continued fractions+ (thanks to Justus Sagemüller)+ - Function instances for Const, Identity, and the types in+ Data.Monoid; instance Functor Fun (thanks to Erik Schnetter+ and Xia Li-yao)+ - More of Test.QuickCheck.Function is exported from+ Test.QuickCheck+ - Semantics of .||. changed to improve short-circuiting:+ if the left argument's precondition is false, the right+ argument is not evaluated and the whole disjunction is+ considered to have a false precondition+ - Bug fix: suchThatMaybe always increased size to at least 1++* Miscellaneous API changes:+ - Result type has changed a bit:+ - InsufficientCovered constructor is gone+ - Type of labels has changed+ - New fields classes, tables++## QuickCheck 2.11.1 - 2.11.3 (released 2018-01-12)+* Cosmetic fixes.++## QuickCheck 2.11 (released 2018-01-12)+* New features:+ - InfiniteList modifier generates infinite lists and shows+ only the relevant part.+ - applyArbitrary2/3/4 for applying a function to random+ arguments.+ - Template Haskell function allProperties returns all+ properties in a module.++* Applicative Gen instances do less splitting.+* Property now has a Typeable instance.+* (===) now prints correct output when the property is true.+* Test.QuickCheck now exports Fun constructor.+* verboseCheck output is now slightly less confusing.++## QuickCheck 2.10.1 (released 2017-10-06)+* Arbitrary instances for Foreign.C.Types are available in more+ GHC versions.+* Fixed a bug where withMaxSuccess didn't adjust the allowed+ number of discarded tests.+* Remove quadratic behaviour in terminal output.++## QuickCheck 2.10 (released 2017-06-15)+* New combinators:+ - withMaxSuccess sets the maximum number of test cases for a property.+ - shrinkMap/shrinkMapBy are helpers for defining shrink functions.+ - total checks that a value is non-crashing.+ - suchThatMap is similar to 'suchThat'+ but takes a Maybe-returning function instead of a predicate.+ - getSize returns the current test case size.++* Random strings and characters now include Unicode characters by+ default. To generate only ASCII characters, use the new+ ASCIIString modifier or arbitraryASCIIChar generator.+ The following modifiers and generators also control the+ kind of strings generated: UnicodeString, PrintableString,+ arbitraryUnicodeChar, arbitraryPrintableChar.++* QuickCheck no longer catches asynchronous exceptions, which+ means that pressing ctrl-C will now cancel testing without+ printing a counterexample. If you are debugging an infinite loop,+ please use the 'within' combinator or 'verboseCheck' instead.+ ('within' is better as it allows the counterexample to be+ shrunk.)++* Much of Test.QuickCheck.Function (showable random functions)+ is now exported from Test.QuickCheck.+ - Test.QuickCheck.Function now defines functions and+ pattern synonyms which simplify testing functions of+ more than one argument: apply2, apply3, Fn2, Fn3.++* New typeclasses Arbitrary1 and Arbitrary2 which lift Arbitrary+ to unary/binary type constructors, like in Data.Functor.Classes.++* Some Arbitrary instances have been removed: NonEmpty, Natural.+ This is because they resulted in a lot of extra dependencies.+ You can now find them in the quickcheck-instances package.+ Alternatively, use the NonEmptyList and NonNegative modifiers.++* New Arbitrary instances for the following types: Proxy, ExitCode,+ WrappedMonad, WrappedArrow, QCGen, and the types in+ Foreign.C.Types and Data.Functor.{Product,Compose}.+ Also a Function instance for Word.++* The functions in Test.QuickCheck.Monadic which take an argument+ of type PropertyM m a now expect that 'a' to be Testable, and test it.+ To reduce breakage from this, () is now an instance of Testable which+ always succeeds.+ - PropertyM now has a MonadFail instance on recent GHCs.+ Furthermore, the constraints on some instances were loosened.++* Miscellaneous API changes:+ - Result now returns the counterexample as a list of strings.+ See the "failingTestCase" field.+ - Args now has a `maxShrinks` argument, the maximum number of+ shrinks to try before giving up shrinking.+ - The 'labels' field of Result now encodes frequencies as Doubles+ rather than Ints.++* Bugfixes:+ - 'Test.QuickCheck.Function', 'Test.QuickCheck.Poly', and+ 'Test.QuickCheck.Monadic' are now Safe modules.+ - Result.theException and Result.reason were taken from+ the pre-shrunk counterexample, not the shrunk one.+ - The Testable Property instance improperly used 'again'.+ - Gen.>>= is strict in the result of split, fixing a space leak.+ - within now gives a better error message on timeout++* Some more examples and links have been added to the documentation.++## QuickCheck 2.9.2 (released 2016-09-15)+* Fix a bug where some properties were only being tested once+* Make shrinking of floating-point values less aggressive+* Add function chooseAny :: Random a => Gen a++## QuickCheck 2.9.1 (released 2016-07-11)+* 'again' was only used in forAllShrink, not forAll++## QuickCheck 2.9 (released 2016-07-10)+* Arbitrary, CoArbitrary and Function instances for more types+* Generics for automatic Function instances+* A new combinator "again" which undoes the effect of "once"+* Remove "exhaustive" from Testable typeclass;+ instead, combinators which are nonexhaustive (such as forAll)+ call "again", which should be more robust++* Drop support for GHC 6.x++* Fixed bugs:+ * arbitrarySizedBoundedIntegral wasn't generating huge integers+ * verboseCheck failed with Test.QuickCheck.Function+ * label had a space leak++## QuickCheck 2.8.2 (released 2016-01-15)+* GHC 8 support+* Add Arbitrary and CoArbitrary instances for types in+ containers package+* Improve speed of shuffle combinator+* Only print to stderr if it's a terminal.+* Small changes: slightly improve documentation,+ remove redundant constraints from some functions' types,+ small improvements to Test.QuickCheck.All.++## QuickCheck 2.8.1 (released 2015-04-03)+* Fix bug where exceptions thrown printing counterexamples weren't+ being caught when terminal output was disabled+* Don't export Test.QuickCheck.Property.result++## 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.++## QuickCheck 2.7.2 (released 2014-03-22)+* Fix bug in cabal file which broke cabal test++## QuickCheck 2.7.1 (released 2014-03-20)+* Fixed bug - the Small modifier didn't work on unsigned types+* Changed arbitrarySizedIntegral to have an Integral constraint+ instead of just Num++## QuickCheck 2.7 (released 2014-03-19)++* New features:+ * New genericShrink function provides generic shrinking with GHC.+ * New combinator x === y: fails if x /= y, but also prints their values+ * New function generate :: Gen a -> IO a for running a generator.+ * New combinators infiniteList and infiniteListOf for generating infinite lists.+ * Several combinators added to the main Test.QuickCheck module which+ were previously languishing in other modules. Of particular interest:+ quickCheckAll, ioProperty.+ * New combinators delay and capture which can be used (unsafely!)+ to reuse the random number seed. Useful for generating+ polymorphic (rank-2) values.+ * A new Discard data type and a Testable instance for discarding test cases.+ * All modifiers now have Functor instances and accessor functions.+ * Pressing ctrl-C during shrinking now shows the last failed+ test case, rather than the current shrinking candidate.+ * Experimental support for UHC. You will need the latest version of Cabal from git.++* Better distribution of test data:+ * The Int generator now only generates fairly small numbers.+ * The new Small and Large modifiers control the distribution of integers+ (Small generates small numbers, Large from the whole range).+ * Floating-point numbers shrink better.++* Improved random number generation:+ * QuickCheck now uses TFGen rather than StdGen on newer versions+ of GHC, because StdGen's random numbers aren't always random.+ * 'variant' now uses a prefix code. This should prevent some+ potential bananaskins with coarbitrary.++* API changes:+ * The Gen monad now uses an abstract type QCGen rather than StdGen.+ * The Result type now returns the thrown exception and number+ of failed shrink attempts.+ * Property is now a newtype rather than Gen Prop as it was before.+ * promote is moved into the new module Test.QuickCheck.Gen.Unsafe.+ * 'printTestCase' is deprecated - its new name is 'counterexample'+ * 'morallyDubiousIOProperty' is deprecated - its new name is+ 'ioProperty', no moral judgement involved :)++## QuickCheck 2.6 (released 2013-03-07)++* Add convenience Function instances for up to 7-tuples+* Make stderr line buffered to reduce console I/O.+* Return a flag to say whether the test case was interrupted.++## QuickCheck 2.5 (released 2012-06-18)++* Replace maxDiscard with maxDiscardRatio+* Remove Testable () instance.+* Added a 'discard' exception that discards the current test case+* Add accessors for modifiers (where it makes sense)+* Rename 'stop' to 'abort' to avoid a name clash+* Added a 'once' combinator+* If a property is of type Bool, only run it once+* Add coarbitraryEnum to Test.QuickCheck module.+* Add 'coarbitrary' helper for Enums.+* Rejiggled the formatting code to support multi-line error messages+* Add instances for Ordering and Fixed.+* Added arbitraryBoundedEnum generator (thanks to Antoine Latter).+* Add verboseCheckAll and polyverboseCheck function for usability.
examples/Heap.hs view
@@ -5,8 +5,6 @@ -- imports import Test.QuickCheck-import Test.QuickCheck.Text-import Test.QuickCheck.All import Data.List ( sort@@ -25,7 +23,7 @@ = Node a (Heap a) (Heap a) | Empty deriving ( Eq, Ord, Show )- + empty :: Heap a empty = Empty @@ -53,13 +51,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]@@ -74,7 +72,7 @@ toSortedList :: Ord a => Heap a -> [a] toSortedList Empty = []-toSortedList (Node x h1 h2) = x : toList (h1 `merge` h2)+toSortedList (Node x h1 h2) = x : toSortedList (h1 `merge` h2) -------------------------------------------------------------------------- -- specification@@ -109,7 +107,7 @@ insert x h ==? (x : toList h) prop_RemoveMin (h :: Heap Int) =- cover (size h > 1) 80 "non-trivial" $+ cover 80 (size h > 1) "non-trivial" $ case removeMin h of Nothing -> h ==? [] Just (x,h') -> x == minimum (toList h) && h' ==? (toList h \\ [x])@@ -124,7 +122,7 @@ h ==? xs && xs == sort xs where xs = toSortedList h- + -------------------------------------------------------------------------- -- generators @@ -141,29 +139,26 @@ where arbHeap2 = arbHeap (Just y) (n `div` 2)) | n > 0 ]- + -------------------------------------------------------------------------- -- main return []-main = $quickCheckAll+main = do True <- $quickCheckAll; return () -------------------------------------------------------------------------- -- the end.-{-- shrink Empty = []- shrink (Node x h1 h2) =- [ h1, h2 ]- ++ [ Node x h1' h2 | h1' <- shrink h1, x <=? h1' ]- ++ [ Node x h1 h2' | h2' <- shrink h2, x <=? h2' ]- ++ [ Node x' h1 h2 | x' <- shrink x, x' <=? h1, x' <=? h2 ]--} +-- shrink Empty = []+-- shrink (Node x h1 h2) =+-- [ h1, h2 ]+-- ++ [ Node x h1' h2 | h1' <- shrink h1, x <=? h1' ]+-- ++ [ Node x h1 h2' | h2' <- shrink h2, x <=? h2' ]+-- ++ [ Node x' h1 h2 | x' <- shrink x, x' <=? h1, x' <=? h2 ]+ -- toSortedList (Node x h1 h2) = x : toSortedList (h1 `merge` h2) -{--prop_HeapIsNotSorted (h :: Heap Int) =- expectFailure $- toList h == toSortedList h--}+-- prop_HeapIsNotSorted (h :: Heap Int) =+-- expectFailure $+-- toList h == toSortedList h
+ examples/Heap_Program.hs view
@@ -0,0 +1,197 @@+{-# LANGUAGE ScopedTypeVariables, TemplateHaskell #-}+module Main where++--------------------------------------------------------------------------+-- imports++import Test.QuickCheck+import Test.QuickCheck.Poly++import Data.List+ ( sort+ , (\\)+ )++import Control.Monad+ ( liftM+ , liftM2+ )++--------------------------------------------------------------------------+-- skew heaps++data Heap a+ = Node a (Heap a) (Heap a)+ | Nil+ deriving ( Eq, Ord, Show )++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)++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)++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 80 (size h > 1) "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 _ h) =+ h ==? xs && xs == sort xs+ where+ xs = toSortedList h++--------------------------------------------------------------------------+-- main++return []+main = do True <- $quickCheckAll; return ()++--------------------------------------------------------------------------+-- the end.++-- toSortedList (Node x h1 h2) = x : toSortedList (h1 `merge` h2)++
+ examples/Heap_ProgramAlgebraic.hs view
@@ -0,0 +1,254 @@+{-# LANGUAGE ScopedTypeVariables, TemplateHaskell, GADTs #-}+module Main where++--------------------------------------------------------------------------+-- imports++import Test.QuickCheck+import Test.QuickCheck.Poly++import Data.List+ ( sort+ , nub+ , (\\)+ )++import Data.Maybe+ ( fromJust+ )++import Control.Monad+ ( liftM+ , liftM2+ )++--------------------------------------------------------------------------+-- skew heaps++data Heap a+ = Node a (Heap a) (Heap a)+ | Nil+ deriving ( Eq, Ord, Show )++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 [p] [] = p+ merging (p:q:ps) qs = merging ps ((p`merge`q):qs)+ merging ps qs = merging (ps ++ reverse qs) []++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 : toSortedList (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)++safeRemoveMin :: Ord a => Heap a -> Heap a+safeRemoveMin h = case removeMin h of+ Nothing -> empty -- arbitrary choice+ Just (_,h) -> h++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) = safeRemoveMin (heap p)+heap (Merge p q) = heap p `merge` heap q+heap (FromList xs) = fromList xs++instance (Ord a, 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 Empty = []+ shrink (Unit x) = [ Unit x' | x' <- shrink x ]+ shrink (FromList xs) = [ Unit x | x <- xs ]+ ++ [ FromList xs' | xs' <- shrink xs ]+ shrink p =+ [ FromList (toList (heap p)) ] +++ case p of+ Insert x p -> [ p ]+ ++ [ Insert x p' | p' <- shrink p ]+ ++ [ Insert x' p | x' <- shrink x ]+ SafeRemoveMin p -> [ p ]+ ++ [ SafeRemoveMin p' | p' <- shrink p ]+ Merge p q -> [ p, q ]+ ++ [ Merge p' q | p' <- shrink p ]+ ++ [ Merge p q' | q' <- shrink q ]++data HeapPP a = HeapPP (HeapP a) (Heap a)+ deriving (Show)++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++data Context a where+ Context :: Eq b => (Heap a -> b) -> Context a++instance (Ord a, Arbitrary a) => Arbitrary (Context a) where+ arbitrary =+ do f <- sized arbContext+ let vec h = (size h, toSortedList h, isEmpty h)+ return (Context (vec . f))+ where+ arbContext s =+ frequency+ [ (1, do return id)+ , (s, do x <- arbitrary+ f <- arbContext (s-1)+ return (insert x . f))+ , (s, do f <- arbContext (s-1)+ return (safeRemoveMin . f))+ , (s, do HeapPP _ h <- arbitrary+ f <- arbContext (s`div`2)+ elements [ (h `merge`) . f, (`merge` h) . f ])+ ]++instance Show (Context a) where+ show _ = "*"++(=~) :: Heap Char -> Heap Char -> Property+--h1 =~ h2 = sort (toList h1) == sort (toList h2)+--h1 =~ h2 = property (nub (sort (toList h1)) == nub (sort (toList h2))) -- bug!+h1 =~ h2 = property (\(Context c) -> c h1 == c h2)++{-+The normal form is:++ insert x1 (insert x2 (... empty)...)++where x1 <= x2 <= ...+-}++-- heap creating operations++prop_Unit x =+ unit x =~ insert x empty++prop_RemoveMin_Empty =+ removeMin (empty :: Heap OrdA) == Nothing++prop_RemoveMin_Insert1 x =+ removeMin (insert x empty :: Heap OrdA) == Just (x, empty)++prop_RemoveMin_Insert2 x y (HeapPP _ h) =+ removeMin (insert x (insert y h)) ==~+ (insert (max x y) `maph` removeMin (insert (min x y) h))+ where+ f `maph` Just (x,h) = Just (x, f h)+ f `maph` Nothing = Nothing++ Nothing ==~ Nothing = property True+ Just (x,h1) ==~ Just (y,h2) = x==y .&&. h1 =~ h2++prop_InsertSwap x y (HeapPP _ h) =+ insert x (insert y h) =~ insert y (insert x h)++prop_MergeInsertLeft x (HeapPP _ h1) (HeapPP _ h2) =+ (insert x h1 `merge` h2) =~ insert x (h1 `merge` h2)++prop_MergeInsertRight x (HeapPP _ h1) (HeapPP _ h2) =+ (h1 `merge` insert x h2) =~ insert x (h1 `merge` h2)++-- heap observing operations++prop_Size_Empty =+ size empty == 0++prop_Size_Insert x (HeapPP _ (h :: Heap OrdA)) =+ size (insert x h) == 1 + size h++prop_ToList_Empty =+ toList empty == ([] :: [OrdA])++prop_ToList_Insert x (HeapPP _ (h :: Heap OrdA)) =+ sort (toList (insert x h)) == sort (x : toList h)++prop_ToSortedList (HeapPP _ (h :: Heap OrdA)) =+ toSortedList h == sort (toList h)++--------------------------------------------------------------------------+-- main++return []+main = do True <- $quickCheckAll; return ()++--------------------------------------------------------------------------+-- the end.+++
+ examples/Lambda.hs view
@@ -0,0 +1,363 @@+{-# LANGUAGE ScopedTypeVariables, TemplateHaskell #-}+module Main where++--------------------------------------------------------------------------+-- imports++import Test.QuickCheck++import Control.Monad+ ( liftM+ , liftM2+ )++import Data.Char+ ( toUpper+ )++import Data.Set (Set)+import qualified Data.Set as Set++--------------------------------------------------------------------------+-- types for lambda expressions++-- variables++newtype Var = MkVar String+ deriving ( Eq, Ord )++instance Show Var where+ show (MkVar s) = s++varList :: [Var]+varList = [ MkVar s+ | let vs = [ c:v | v <- "" : vs, c <- ['a'..'z'] ]+ , s <- vs+ ]++instance Arbitrary Var where+ arbitrary = growingElements [ MkVar [c] | c <- ['a'..'z'] ]++-- constants++newtype Con = MkCon String+ deriving ( Eq, Ord )++instance Show Con where+ show (MkCon s) = s++instance Arbitrary Con where+ arbitrary = growingElements [ MkCon [c] | c <- ['A'..'Z'] ]++-- expressions++data Exp+ = Lam Var Exp+ | App Exp Exp+ | Var Var+ | Con Con+ deriving ( Eq, Ord )++instance Show Exp where+ showsPrec n (Lam x t) = showParen (n>0) (showString "\\" . shows x . showString "." . shows t)+ showsPrec n (App s t) = showParen (n>1) (showsPrec 1 s . showString " " . showsPrec 2 t)+ showsPrec _ (Var x) = shows x+ showsPrec _ (Con c) = shows c++instance Arbitrary Exp where+ arbitrary = sized arbExp+ where+ arbExp n =+ frequency $+ [ (2, liftM Var arbitrary)+ , (1, liftM Con arbitrary)+ ] +++ concat+ [ [ (5, liftM2 Lam arbitrary arbExp1)+ , (5, liftM2 App arbExp2 arbExp2)+ ]+ | n > 0+ ]+ where+ arbExp1 = arbExp (n-1)+ arbExp2 = arbExp (n `div` 2)++ shrink (Lam x a) = [ a ]+ ++ [ Lam x a' | a' <- shrink a ]+ shrink (App a b) = [ a, b ]+ ++ [ ab+ | Lam x a' <- [a]+ , let ab = subst x b a'+ , length (show ab) < length (show (App a b))+ ]+ ++ [ App a' b | a' <- shrink a ]+ ++ [ App a b' | b' <- shrink b ]+ shrink (Var x) = [Con (MkCon (map toUpper (show x)))]+ shrink _ = []++--------------------------------------------------------------------------+-- functions for lambda expressions++free :: Exp -> Set Var+free (Lam x a) = Set.delete x (free a)+free (App a b) = free a `Set.union` free b+free (Var x) = Set.singleton x+free (Con _) = Set.empty++subst :: Var -> Exp -> Exp -> Exp+subst x c (Var y) | x == y = c+subst x b (Lam y a) | x /= y = Lam y (subst x b a)+subst x c (App a b) = App (subst x c a) (subst x c b)+subst x c a = a++fresh :: Var -> Set Var -> Var+fresh x ys = head (filter (`Set.notMember` ys) (x:varList))++rename :: Var -> Var -> Exp -> Exp+rename x y a | x == y = a+ | otherwise = subst x (Var y) a++-- different bugs:+--subst x b (Lam y a) | x /= y = Lam y (subst x b a) -- bug 1+--subst x b (Lam y a) | x /= y = Lam y' (subst x b (rename y y' a)) where y':_ = (y:varList) \\ free b -- bug 2+--subst x b (Lam y a) | x /= y = Lam y' (subst x b (rename y y' a)) where y' = (y:varList) \\ (x:free b) -- bug 3+--subst x b (Lam y a) | x /= y = Lam y' (subst x b (rename y y' a)) where y' = fresh y (x:free b) -- bug 4+--subst x c (Lam y a) | x /= y = Lam y' (subst x c (rename y y' a)) where y' = fresh y (x `insert` delete y (free a) `union` free c)++--------------------------------------------------------------------------+-- properties for substitutions++showResult :: (Show a, Testable prop) => a -> (a -> prop) -> Property+showResult x f =+ whenFail (putStrLn ("Result: " ++ show x)) $+ f x++prop_SubstFreeNoVarCapture a x b =+ showResult (subst x b a) $ \subst_x_b_a ->+ x `Set.member` free_a ==>+ free subst_x_b_a == (Set.delete x free_a `Set.union` free b)+ where+ free_a = free a++prop_SubstNotFreeSame a x b =+ showResult (subst x b a) $ \subst_x_b_a ->+ x `Set.notMember` free a ==>+ subst_x_b_a == a++prop_SubstNotFreeSameVars a x b =+ showResult (subst x b a) $ \subst_x_b_a ->+ x `Set.notMember` free a ==>+ free subst_x_b_a == free a++main1 =+ do quickCheck prop_SubstFreeNoVarCapture+ quickCheck prop_SubstNotFreeSame+ quickCheck prop_SubstNotFreeSameVars++--expectFailure $+++++++++--------------------------------------------------------------------------+-- eval++eval :: Exp -> Exp+eval (Var x) = error "eval: free variable"+eval (App a b) =+ case eval a of+ Lam x a' -> eval (subst x b a')+ a' -> App a' (eval b)+eval a = a++--------------------------------------------------------------------------+-- closed lambda expressions++newtype ClosedExp = Closed Exp deriving ( Show )++instance Arbitrary ClosedExp where+ arbitrary = Closed `fmap` sized (arbExp [])+ where+ arbExp xs n =+ frequency $+ [ (8, liftM Var (elements xs))+ | not (null xs)+ ] +++ [ (2, liftM Con arbitrary)+ ] +++ [ (20, do x <- arbitrary+ t <- arbExp (x:xs) n'+ return (Lam x t))+ | n > 0 || null xs+ ] +++ [ (20, liftM2 App (arbExp xs n2) (arbExp xs n2))+ | n > 0+ ]+ where+ n' = n-1+ n2 = n `div` 2++ shrink (Closed a) =+ [ Closed a' | a' <- shrink a, Set.null (free a') ]++--------------------------------------------------------------------------+-- properties for closed lambda expressions++isValue :: Exp -> Bool+isValue (Var _) = False+isValue (App (Lam _ _) _) = False+isValue (App a b) = isValue a && isValue b+isValue _ = True++prop_ClosedExpIsClosed (Closed a) =+ Set.null (free a)++prop_EvalProducesValue (Closed a) =+ within 1000 $+ isValue (eval a)++main2 =+ do quickCheck prop_ClosedExpIsClosed+ quickCheck prop_EvalProducesValue++-- expectFailure $++--------------------------------------------------------------------------+-- main++main =+ do main1+ main2++--------------------------------------------------------------------------+-- the end.++{-+instance Arbitrary Exp where+ arbitrary = sized (arbExp [])+ where++ arbitrary = repair [] `fmap` sized arbExp+ where+ arbExp n =+ frequency $+ [ (1, liftM Var arbitrary)+ ] ++ concat+ [ [ (3, liftM2 Lam arbitrary (arbExp n'))+ , (4, liftM2 App (arbExp n2) (arbExp n2))+ ]+ | n > 0+ ]+ where+ n' = n-1+ n2 = n `div` 2++ repair xs (Var x)+ | x `elem` xs = Var x+ | null xs = Lam x (Var x)+ | otherwise = Var (xs !! (ord (last (show x)) `mod` length xs))+ repair xs (App a b) = App (repair xs a) (repair xs b)+ repair xs (Lam x a) = Lam x (repair (x:xs) a)++ -- lots of clever shrinking added+ shrinkRec (Lam x a) = [ a | x `notElem` free a ]+ shrinkRec (App a b) = [ a, b ]+ ++ [ red+ | Lam x a' <- [a]+ , let red = subst x b a'+ , length (show red) < length (show (App a b))+ ]+ shrinkRec (Var x) = [Con (MkCon (map toUpper (show x)))]+ shrinkRec _ = []++-- types++data Type+ = Base Con+ | Type :-> Type+ deriving ( Eq, Show )++instance Arbitrary Type where+ arbitrary = sized arbType+ where+ arbType n =+ frequency $+ [ (1, liftM Base arbitrary)+ ] +++ [ (4, liftM2 (:->) arbType2 arbType2)+ | n > 0+ ]+ where+ arbType2 = arbType (n `div` 2)++newtype WellTypedExp = WellTyped Exp+ deriving ( Eq, Show )++arbExpWithType n env t =+ frequency $+ [ (2, liftM Var (elements xs))+ | let xs = [ x | (x,t') <- env, t == t' ]+ , not (null xs)+ ] +++ [ (1, return (Con b))+ | Base b <- [t]+ ] +++ [ (if n > 0 then 5 else 1+ , do x <- arbitrary+ b <- arbExpWithType n1 ((x,ta):[ xt | xt <- env, fst xt /= x ]) tb+ return (Lam x b))+ | ta :-> tb <- [t]+ ] +++ [ (5, do tb <- arbitrary+ a <- arbExpWithType n2 env (tb :-> t)+ b <- arbExpWithType n2 env tb+ return (App a b))+ | n > 0+ ]+ where+ n1 = n-1+ n2 = n `div` 2++instance Arbitrary WellTypedExp where+ arbitrary =+ do t <- arbitrary+ e <- sized (\n -> arbExpWithType n [] t)+ return (WellTyped e)++ shrink _ = []++newtype OpenExp = Open Exp+ deriving ( Eq, Show )++instance Arbitrary OpenExp where+ arbitrary = Open `fmap` sized arbExp+ where+ arbExp n =+ frequency $+ [ (2, liftM Var arbitrary)+ , (1, liftM Con arbitrary)+ ] +++ concat+ [ [ (5, liftM2 Lam arbitrary arbExp1)+ , (5, liftM2 App arbExp2 arbExp2)+ ]+ | n > 0+ ]+ where+ arbExp1 = arbExp (n-1)+ arbExp2 = arbExp (n `div` 2)++ shrink (Open a) = map Open (shrink a)++prop_EvalProducesValueWT (WellTyped a) =+ isValue (eval a)++-}++x = MkVar "x"+y = MkVar "y"+
+ examples/Merge.hs view
@@ -0,0 +1,116 @@+{-# LANGUAGE ScopedTypeVariables, TemplateHaskell #-}+module Main where++--------------------------------------------------------------------------+-- imports++import Test.QuickCheck++import Data.List+ ( sort+ )++--------------------------------------------------------------------------+-- merge sort++msort :: Ord a => [a] -> [a]+msort xs = merging [ [x] | x <- xs ]++merging :: Ord a => [[a]] -> [a]+merging [] = []+merging [xs] = xs+merging xss = merging (sweep xss)++sweep :: Ord a => [[a]] -> [[a]]+sweep [] = []+sweep [xs] = [xs]+sweep (xs:ys:xss) = merge xs ys : sweep xss++merge :: Ord a => [a] -> [a] -> [a]+merge xs [] = xs+merge [] ys = ys+merge (x:xs) (y:ys)+ | x <= y = x : merge xs (y:ys)+ | otherwise = y : merge (x:xs) ys++--------------------------------------------------------------------------+-- example properties++ordered :: Ord a => [a] -> Bool+ordered [] = True+ordered [x] = True+ordered (x:y:xs) = x <= y && ordered (y:xs)++prop_Merge xs (ys :: [Int]) =+ ordered xs && ordered ys ==>+ collect (length xs + length ys) $+ ordered (xs `merge` ys)++-- collect (sort [length xs, length ys]) $++++++++++++++++++++--------------------------------------------------------------------------+-- quantificiation++--prop_Merge (Ordered xs) (Ordered (ys :: [Int])) =+-- ordered (xs `merge` ys)++++++++++++++-- classify (length xs `min` length ys >= 5) "not trivial" $+-- cover (length xs `min` length ys >= 5) 70 "not trivial" $++{-+ shrink (Ordered xs) =+ [ Ordered xs'+ | xs' <- shrink xs+ , ordered xs'+ ]+-}++--------------------------------------------------------------------------+-- merging++prop_Merging (xss :: [OrderedList Int]) =+ ordered (merging [ xs | Ordered xs <- xss ])++++++++-- mapSize (`div` 2) $ \(xss :: [OrderedList Int]) ->++return []+main = do True <- $quickCheckAll; return ()++--------------------------------------------------------------------------+-- the end.
+ examples/Set.hs view
@@ -0,0 +1,207 @@+{-# LANGUAGE ScopedTypeVariables, TemplateHaskell #-}+module Main where++--------------------------------------------------------------------------+-- imports++import Test.QuickCheck++import Text.Show.Functions+import Data.List+ ( sort+ , group+ , nub+ , (\\)+ )++import Control.Monad+ ( liftM+ , liftM2+ )++import Data.Maybe++--------------------------------------------------------------------------+-- binary search trees++data Set a+ = Node a (Set a) (Set a)+ | Empty+ deriving ( Eq, Ord, Show )++empty :: Set a+empty = Empty++isEmpty :: Set a -> Bool+isEmpty Empty = True+isEmpty _ = False++unit :: a -> Set a+unit x = Node x empty empty++size :: Set a -> Int+size Empty = 0+size (Node _ s1 s2) = 1 + size s1 + size s2++insert :: Ord a => a -> Set a -> Set a+insert x s = s `union` unit x++merge :: Set a -> Set a -> Set a+s `merge` Empty = s+s `merge` Node x Empty s2 = Node x s s2+s `merge` Node x (Node y s11 s12) s2 = Node y s (Node x (s11 `merge` s12) s2)++delete :: Ord a => a -> Set a -> Set a+delete x Empty = Empty+delete x (Node x' s1 s2) =+ case x `compare` x' of+ LT -> Node x' (delete x s1) s2+ EQ -> s1 `merge` s2+ GT -> Node x' s1 (delete x s2)++union :: Ord a => Set a -> Set a -> Set a+{-+s1 `union` Empty = s1+Empty `union` s2 = s2+s1@(Node x s11 s12) `union` s2@(Node y s21 s22) =+ case x `compare` y of+ LT -> Node x s11 (s12 `union` Node y Empty s22) `union` s21+ EQ -> Node x (s11 `union` s21) (s12 `union` s22)+ --GT -> s11 `union` Node y s21 (Node x Empty s12 `union` s22)+ GT -> Node x (s11 `union` Node y s21 Empty) s12 `union` s22+-}+s1 `union` Empty = s1+Empty `union` s2 = s2+Node x s11 s12 `union` s2 = Node x (s11 `union` s21) (s12 `union` s22)+ where+ (s21,s22) = split x s2++split :: Ord a => a -> Set a -> (Set a, Set a)+split x Empty = (Empty, Empty)+split x (Node y s1 s2) =+ case x `compare` y of+ LT -> (s11, Node y s12 s2)+ EQ -> (s1, s2)+ GT -> (Node y s1 s21, s22)+ where+ (s11,s12) = split x s1+ (s21,s22) = split x s2++mapp :: (a -> b) -> Set a -> Set b+mapp f Empty = Empty+mapp f (Node x s1 s2) = Node (f x) (mapp f s1) (mapp f s2)++fromList :: Ord a => [a] -> Set a+--fromList xs = build [ (empty,x) | x <- sort xs ]+fromList xs = build [ (empty,head x) | x <- group (sort xs) ]+ where+ build [] = empty+ build [(s,x)] = attach x s+ build sxs = build (sweep sxs)++ sweep [] = []+ sweep [sx] = [sx]+ sweep ((s1,x1):(s2,x2):sxs) = (Node x1 s1 s2,x2) : sweep sxs++ attach x Empty = unit x+ attach x (Node y s1 s2) = Node y s1 (attach x s2)++toList :: Set a -> [a]+toList s = toSortedList s++toSortedList :: Set a -> [a]+toSortedList s = toList' s []+ where+ toList' Empty xs = xs+ toList' (Node x s1 s2) xs = toList' s1 (x : toList' s2 xs)++--------------------------------------------------------------------------+-- generators++instance (Ord a, Arbitrary a) => Arbitrary (Set a) where+ arbitrary = sized (arbSet Nothing Nothing)+ where+ arbSet mx my n =+ frequency $+ [ (1, return Empty) ] +++ [ (7, do mz <- arbitrary `suchThatMaybe` (isOK mx my)+ case mz of+ Nothing -> return Empty+ Just z -> liftM2 (Node z) (arbSet mx mz n2)+ (arbSet mz my n2)+ where n2 = n `div` 2)+ | n > 0+ ]++ isOK mx my z =+ maybe True (<z) mx && maybe True (z<) my++ shrink Empty = []+ shrink t@(Node x s1 s2) = [ s1, s2 ]+ ++ [ t' | x' <- shrink x, let t' = Node x' s1 s2, invariant t' ]++--------------------------------------------------------------------------+-- properties++(.<) :: Ord a => Set a -> a -> Bool+Empty .< x = True+Node y _ s .< x = y < x && s .< x++(<.) :: Ord a => a -> Set a -> Bool+x <. Empty = True+x <. Node y _ s = x < y && x <. s++(==?) :: Ord a => Set a -> [a] -> Bool+s ==? xs = invariant s && sort (toList s) == nub (sort xs)++invariant :: Ord a => Set a -> Bool+invariant Empty = True+invariant (Node x s1 s2) = s1 .< x && x <. s2 && invariant s1 && invariant s2++prop_Invariant (s :: Set Int) =+ invariant s++prop_Empty =+ empty ==? ([] :: [Int])++prop_Unit (x :: Int) =+ unit x ==? [x]++prop_Size (s :: Set Int) =+ cover 60 (size s >= 15) "large" $+ size s == length (toList s)++prop_Insert x (s :: Set Int) =+ insert x s ==? (x : toList s)++prop_Delete x (s :: Set Int) =+ delete x s ==? (toList s \\ [x])++prop_Union s1 (s2 :: Set Int) =+ (s1 `union` s2) ==? (toList s1 ++ toList s2)++prop_Mapp (f :: Int -> Int) (s :: Set Int) =+ expectFailure $+ whenFail (putStrLn ("Fun: " ++ show [ (x,f x) | x <- toList s])) $+ mapp f s ==? map f (toList s)++prop_FromList (xs :: [Int]) =+ fromList xs ==? xs++prop_ToSortedList (s :: Set Int) =+ s ==? xs && xs == sort xs+ where+ xs = toSortedList s++prop_FromList' (xs :: [Int]) =+ shrinking shrink xs $ \xs' ->+ fromList xs ==? xs++--------------------------------------------------------------------------+-- main++return []+main = do True <- $quickCheckAll; return ()++--------------------------------------------------------------------------+-- the end.
+ examples/Simple.hs view
@@ -0,0 +1,46 @@+{-# LANGUAGE ScopedTypeVariables, TemplateHaskell #-}+module Main where++--------------------------------------------------------------------------+-- imports++import Test.QuickCheck++--------------------------------------------------------------------------+-- example 1++allEqual x y z = x == y && y == z+allEqual' x y z = 2*x == y + z++prop_SimonThompson x y (z :: Int) =+ allEqual x y z == allEqual' x y z++--------------------------------------------------------------------------+-- example 2++prop_ReverseReverse :: Eq a => [a] -> Bool+prop_ReverseReverse xs =+ reverse (reverse xs) == xs++prop_Reverse xs =+ reverse xs == xs++--------------------------------------------------------------------------+-- example 3++prop_Error (x,y) =+ 2*x <= 5*y++--------------------------------------------------------------------------+-- main++return []+prop_conj = counterexample "Simon Thompson" $(monomorphic 'prop_SimonThompson) .&&.+ counterexample "reverse" $(monomorphic 'prop_Reverse)+prop_disj = counterexample "reverse" $(monomorphic 'prop_Reverse) .||.+ counterexample "Simon Thompson" $(monomorphic 'prop_SimonThompson)+return []+main = do True <- $quickCheckAll; return ()++--------------------------------------------------------------------------+-- the end.
+ make-hugs view
@@ -0,0 +1,28 @@+#!/bin/bash++set -e++TOPDIR=$(dirname "$0")+TARGETDIR=$TOPDIR/quickcheck-hugs++find "$TOPDIR/src" -name '*.hs' | while read -r src; do+ tgt="$TARGETDIR/$(echo "$src" | sed "s/^$TOPDIR\/src"'//')"++ echo "Processing $src -> $tgt"++ mkdir -p "$(dirname "$tgt")"+ # If you want to switch on and off other features, look in+ # QuickCheck.cabal to see what's available, or submit a patch+ # adding a new -DNO_... flag.+ cpphs --noline -DOLD_RANDOM -DNO_SPLITMIX -DNO_TEMPLATE_HASKELL \+ -DNO_CTYPES_CONSTRUCTORS -DNO_FOREIGN_C_USECONDS -DNO_GENERICS \+ -DNO_SAFE_HASKELL -DNO_POLYKINDS -DNO_MONADFAIL -DNO_TIMEOUT \+ -DNO_NEWTYPE_DERIVING -DNO_TYPEABLE -DNO_GADTS -DNO_TRANSFORMERS \+ -DNO_DEEPSEQ -DNO_EXTRA_METHODS_IN_APPLICATIVE -DNO_CALLSTACK \+ -DNO_SEMIGROUP -DNO_EXISTENTIAL_FIELD_SELECTORS \+ "$src" > "$tgt"+done++echo "A Hugs-compatible version of QuickCheck is now"+echo "available in the quickcheck-hugs directory."+echo "Load it with hugs -98."
+ src/Test/QuickCheck.hs view
@@ -0,0 +1,381 @@+{-|+The <http://www.cse.chalmers.se/~rjmh/QuickCheck/manual.html QuickCheck manual>+gives detailed information about using QuickCheck effectively.+You can also try <https://begriffs.com/posts/2017-01-14-design-use-quickcheck.html>,+a tutorial written by a user of QuickCheck.++To start using QuickCheck, write down your property as a function returning @Bool@.+For example, to check that reversing a list twice gives back the same list you can write:++@+import Test.QuickCheck++prop_reverse :: [Int] -> Bool+prop_reverse xs = reverse (reverse xs) == xs+@++You can then use QuickCheck to test @prop_reverse@ on 100 random lists:++>>> quickCheck prop_reverse++++ OK, passed 100 tests.++To run more tests you can use the 'withNumTests' combinator:++>>> quickCheck (withNumTests 10000 prop_reverse)++++ OK, passed 10000 tests.++To use QuickCheck on your own data types you will need to write 'Arbitrary'+instances for those types. See the+<http://www.cse.chalmers.se/~rjmh/QuickCheck/manual.html QuickCheck manual> for+details about how to do that.++When testing fails @quickCheck@ will try to give you a minimal counterexample to+your property:+@+import Test.QuickCheck++prop_reverse_bad :: [Int] -> Bool+prop_reverse_bad xs = reverse xs == xs++>>> quickCheck prop_reverse_bad+*** Failed! Falsified (after 3 tests and 3 shrinks):+[0,1]+@++However, beware because not all properties that ought to fail will fail when you expect+them to:++@+>>> quickCheck $ \ x y -> x == y++++ Ok, passed 100 tests.+@++That's because GHCi will default any type variables in your property to @()@, so in the example+above @quickCheck@ was really testing that @()@ is equal to itself. To avoid this behaviour it+is best practise to monomorphise your polymorphic properties when testing:++@+>>> quickCheck $ \ x y -> (x :: Int) == y+*** Failed! Falsified (after 4 tests and 3 shrinks):+0+1+@++-}+{-# LANGUAGE CPP #-}+#ifndef NO_SAFE_HASKELL+{-# LANGUAGE Safe #-}+#endif+#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ >= 708+{-# LANGUAGE PatternSynonyms #-}+#endif+module Test.QuickCheck+ (+ -- * Running tests+ quickCheck+ , Args(..), Result(..), TestProgress(..)+ , stdArgs+ , quickCheckWith+ , quickCheckWithResult+ , quickCheckResult+ , recheck+ , isSuccess+ -- ** Running tests verbosely+ , verboseCheck+ , verboseCheckWith+ , verboseCheckWithResult+ , verboseCheckResult+#ifndef NO_TEMPLATE_HASKELL+ -- ** Testing all properties in a module++ -- | These functions test all properties in the current module, using+ -- Template Haskell. You need to have a @{-\# LANGUAGE TemplateHaskell \#-}@+ -- pragma in your module for any of these to work.+ , quickCheckAll+ , verboseCheckAll+ , forAllProperties+ , allProperties+ -- ** Testing polymorphic properties+ , polyQuickCheck+ , polyVerboseCheck+ , monomorphic+#endif++ -- * The 'Arbitrary' typeclass: generation of random values+ , Arbitrary(..)+ -- ** Helper functions for implementing 'shrink'+#ifndef NO_GENERICS+ , genericShrink+ , subterms+ , recursivelyShrink+#endif+ , shrinkNothing+ , shrinkList+ , shrinkMap+ , shrinkMapBy+ , shrinkIntegral+ , shrinkRealFrac+ , shrinkBoundedEnum+ , shrinkDecimal++ -- ** Lifting of 'Arbitrary' to unary and binary type constructors+ , Arbitrary1(..)+ , arbitrary1+ , shrink1+ , Arbitrary2(..)+ , arbitrary2+ , shrink2++ -- * The 'Gen' monad: combinators for building random generators+ , Gen+ -- ** Generator combinators+ , choose+ , chooseInt+ , chooseInteger+#ifndef NO_SPLITMIX+ , chooseWord64+ , chooseInt64+ , chooseUpTo+#endif+ , chooseBoundedIntegral+ , chooseEnum+ , chooseAny+ , oneof+ , frequency+ , elements+ , growingElements+ , sized+ , getSize+ , resize+ , scale+ , suchThat+ , suchThatMap+ , suchThatMaybe+ , applyArbitrary2+ , applyArbitrary3+ , applyArbitrary4+ -- ** Generators for lists+ , listOf+ , listOf1+ , vectorOf+ , vector+ , infiniteListOf+ , infiniteList+ , shuffle+ , sublistOf+ , orderedList+ -- ** Generators for particular types+ , arbitrarySizedIntegral+ , arbitrarySizedNatural+ , arbitrarySizedFractional+ , arbitrarySizedBoundedIntegral+ , arbitraryBoundedIntegral+ , arbitraryBoundedRandom+ , arbitraryBoundedEnum+ , arbitraryUnicodeChar+ , arbitraryASCIIChar+ , arbitraryPrintableChar+ -- ** Running generators+ , generate+ -- ** Debugging generators+ , sample+ , sample'++#ifndef NO_GADTS+ -- * The 'Function' typeclass: generation of random shrinkable, showable functions++ -- | Example of use:+ --+ -- >>> :{+ -- >>> let prop :: Fun String Integer -> Bool+ -- >>> prop (Fun _ f) = f "monkey" == f "banana" || f "banana" == f "elephant"+ -- >>> :}+ -- >>> quickCheck prop+ -- *** Failed! Falsified (after 3 tests and 134 shrinks):+ -- {"elephant"->1, "monkey"->1, _->0}+ --+ -- To generate random values of type @'Fun' a b@,+ -- you must have an instance @'Function' a@.+ -- If your type has a 'Show' instance, you can use 'functionShow' to write the instance; otherwise,+ -- use 'functionMap' to give a bijection between your type and a type that is already an instance of 'Function'.+ -- See the @'Function' [a]@ instance for an example of the latter.+ --+ -- For more information, see the paper \"Shrinking and showing functions\" by Koen Claessen.+ , Fun (..)+ , applyFun+ , applyFun2+ , applyFun3+#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ >= 708+ , pattern Fn+ , pattern Fn2+ , pattern Fn3+#endif+ , Function (..)+ , functionMap+ , functionShow+ , functionIntegral+ , functionRealFrac+ , functionBoundedEnum+ , functionVoid+#endif++ -- * The 'CoArbitrary' typeclass: generation of functions the old-fashioned way+ , CoArbitrary(..)+#ifndef NO_GENERICS+ , genericCoarbitrary+#endif+ , variant+ , coarbitraryIntegral+ , coarbitraryReal+ , coarbitraryShow+ , coarbitraryEnum+ , (><)++ -- * Type-level modifiers for changing generator behavior++ -- | These types do things such as restricting the kind of test data that can be generated.+ -- They can be pattern-matched on in properties as a stylistic+ -- alternative to using explicit quantification.+ --+ -- Examples:+ --+ -- @+ -- -- Functions cannot be shown (but see 'Function')+ -- prop_TakeDropWhile ('Blind' p) (xs :: ['Test.QuickCheck.Poly.A']) =+ -- takeWhile p xs ++ dropWhile p xs == xs+ -- @+ --+ -- @+ -- prop_TakeDrop ('NonNegative' n) (xs :: ['Test.QuickCheck.Poly.A']) =+ -- take n xs ++ drop n xs == xs+ -- @+ --+ -- @+ -- -- cycle does not work for empty lists+ -- prop_Cycle ('NonNegative' n) ('NonEmpty' (xs :: ['Test.QuickCheck.Poly.A'])) =+ -- take n (cycle xs) == take n (xs ++ cycle xs)+ -- @+ --+ -- @+ -- -- Instead of 'forAll' 'orderedList'+ -- prop_Sort ('Ordered' (xs :: ['Test.QuickCheck.Poly.OrdA'])) =+ -- sort xs == xs+ -- @+ , Blind(..)+ , Fixed(..)+ , OrderedList(..)+ , NonEmptyList(..)+ , InfiniteList(..)+ , SortedList(..)+ , Positive(..)+ , Negative(..)+ , NonZero(..)+ , NonNegative(..)+ , NonPositive(..)+ , Large(..)+ , Small(..)+ , Smart(..)+ , Shrink2(..)+#ifndef NO_MULTI_PARAM_TYPE_CLASSES+ , Shrinking(..)+ , ShrinkState(..)+#endif+ , ASCIIString(..)+ , UnicodeString(..)+ , PrintableString(..)++ -- * Property combinators+ , Property, Testable(..)+ , forAll+ , forAllShrink+ , forAllShow+ , forAllShrinkShow+ , forAllBlind+ , forAllShrinkBlind+ , shrinking+ , (==>)+ , Discard(..)+ , discard+ , (===)+ , (=/=)+#ifndef NO_DEEPSEQ+ , total+#endif+ , ioProperty+ , idempotentIOProperty+ -- ** Controlling property execution+ , verbose+ , verboseShrinking+ , noShrinking+ , withNumTests+ , withMaxSuccess+ , within+ , discardAfter+ , withDiscardRatio+ , withMaxSize+ , withMaxShrinks+ , once+ , again+ , mapSize+ -- ** Conjunction and disjunction+ , (.&.)+ , (.&&.)+ , conjoin+ , Every (..)+ , (.||.)+ , disjoin+ , Some (..)+ -- ** What to do on failure+#ifndef NO_TYPEABLE+ , Witness(..)+ , witness+ , coerceWitness+ , castWitness+#endif+ , counterexample+ , printTestCase+ , withProgress+ , whenFail+ , whenFail'+ , expectFailure+ -- * Analysing test case distribution+ , label+ , collect+ , classify+ , tabulate+ -- ** Checking test case distribution+ , cover+ , coverTable+ , checkCoverage+ , checkCoverageWith+ , Confidence(..)+ , stdConfidence+ -- ** Generating example test cases+ , labelledExamples+ , labelledExamplesWith+ , labelledExamplesWithResult+ , labelledExamplesResult+ )+ where++--------------------------------------------------------------------------+-- imports++import Test.QuickCheck.Gen+import Test.QuickCheck.Arbitrary+import Test.QuickCheck.Modifiers+import Test.QuickCheck.Monoids+import Test.QuickCheck.Property hiding ( Result(..) )+import Test.QuickCheck.Test+import Test.QuickCheck.Exception+#ifndef NO_GADTS+import Test.QuickCheck.Function+#endif+import Test.QuickCheck.Features+import Test.QuickCheck.State+#ifndef NO_TEMPLATE_HASKELL+import Test.QuickCheck.All+#endif++--------------------------------------------------------------------------+-- the end.
+ src/Test/QuickCheck/All.hs view
@@ -0,0 +1,229 @@+{-# LANGUAGE Rank2Types, CPP #-}+#if __GLASGOW_HASKELL__ >= 800+{-# LANGUAGE TemplateHaskellQuotes #-}+#else+{-# LANGUAGE TemplateHaskell #-}+#endif+#ifndef NO_SAFE_HASKELL+{-# LANGUAGE Trustworthy #-}+#endif++-- | __Note__: the contents of this module are re-exported by+-- "Test.QuickCheck". You do not need to import it directly.+--+-- Test all properties in the current module, using Template Haskell.+-- You need to have a @{-\# LANGUAGE TemplateHaskell \#-}@ pragma in+-- your module for any of these to work.+module Test.QuickCheck.All(+ -- ** Testing all properties in a module+ quickCheckAll,+ verboseCheckAll,+ forAllProperties,+ allProperties,+ runQuickCheckAll,+ -- ** Testing polymorphic properties+ polyQuickCheck,+ polyVerboseCheck,+ monomorphic) where++import Language.Haskell.TH+import Test.QuickCheck.Property hiding (Result)+import Test.QuickCheck.Test+import Data.Char+import Data.List (isPrefixOf, nubBy)+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.+-- Note that just evaluating @'quickCheck' prop@ in GHCi will seem to+-- work, but will silently default all type variables to @()@!+--+-- @$('polyQuickCheck' \'prop)@ means the same as+-- @'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 |] `appE` 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 |] `appE` monomorphic x++type Error = forall a. String -> a++-- | Monomorphise an arbitrary property by defaulting all type variables to 'Integer'.+--+-- 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)+ let err msg = error $ msg ++ ": " ++ pprint ty0+ (polys, ctx, ty) <- deconstructType err ty0+ case polys of+ [] -> return (expName t)+ _ -> do+ integer <- [t| Integer |]+ ty' <- monomorphiseType err integer ty+ return (SigE (expName t) ty')++expName :: Name -> Exp+expName n = if isVar n then VarE n else ConE n++-- See section 2.4 of the Haskell 2010 Language Report, plus support for "[]"+isVar :: Name -> Bool+isVar = let isVar' (c:_) = not (isUpper c || c `elem` ":[")+ isVar' _ = True+ in isVar' . nameBase++infoType :: Info -> Type+#if MIN_VERSION_template_haskell(2,11,0)+infoType (ClassOpI _ ty _) = ty+infoType (DataConI _ ty _) = ty+infoType (VarI _ ty _) = ty+#else+infoType (ClassOpI _ ty _ _) = ty+infoType (DataConI _ ty _ _) = ty+infoType (VarI _ ty _ _) = ty+#endif++deconstructType :: Error -> Type -> Q ([Name], Cxt, Type)+deconstructType err (ForallT xs ctx ty) = do+#if MIN_VERSION_template_haskell(2,17,0)+ let plain (PlainTV nm _) = return nm+ plain (KindedTV nm _ StarT) = return nm+#else+ let plain (PlainTV nm) = return nm+# if MIN_VERSION_template_haskell(2,8,0)+ plain (KindedTV nm StarT) = return nm+# else+ plain (KindedTV nm StarK) = return nm+# endif+#endif+ plain _ = err "Higher-kinded type variables in type"+ xs' <- mapM plain xs+ return (xs', ctx, ty)+deconstructType _ ty = return ([], [], ty)++monomorphiseType :: Error -> Type -> Type -> TypeQ+monomorphiseType err mono ty@(VarT n) = return mono+monomorphiseType err mono (AppT t1 t2) = liftM2 AppT (monomorphiseType err mono t1) (monomorphiseType err mono t2)+monomorphiseType err mono ty@(ForallT _ _ _) = err $ "Higher-ranked type"+monomorphiseType err mono ty = return ty++-- | Test all properties in the current module, using a custom+-- 'quickCheck' function. The same caveats as with 'quickCheckAll'+-- apply.+--+-- @$'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 = [| runQuickCheckAll |] `appE` allProperties++-- | List all properties in the current module.+--+-- @$'allProperties'@ has type @[('String', 'Property')]@.+--+-- 'allProperties' has the same issue with scoping as 'quickCheckAll':+-- see the note there about @return []@.+allProperties :: Q Exp+allProperties = do+ Loc { loc_filename = filename } <- location+ when (filename == "<interactive>") $ error "don't run this interactively"+ ls <- runIO (fmap lines (readUTF8File filename))+ let prefixes = map (takeWhile (\c -> isAlphaNum c || c == '_' || c == '\'') . dropWhile (\c -> isSpace c || c == '>')) ls+ idents = nubBy (\x y -> snd x == snd y) (filter (("prop_" `isPrefixOf`) . snd) (zip [1..] prefixes))+#if MIN_VERSION_template_haskell(2,8,0)+ 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 <- (warning x >> return False) `recover` (reify (mkName x) >> return True)+ if exists+ then sequence+ [ tupE+ [ stringE $ x ++ " from " ++ filename ++ ":" ++ show l+ , [| property |] `appE` monomorphic (mkName x)+ ]+ ]+ else return []+ fmap (ListE . concat) (mapM quickCheckOne idents) `sigE` [t| [(String, Property)] |]++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.+--+-- 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 and later; 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 `appE` [| 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 `appE` [| verboseCheckResult |]++runQuickCheckAll :: [(String, Property)] -> (Property -> IO Result) -> IO Bool+runQuickCheckAll ps qc =+ fmap and . forM ps $ \(xs, p) -> do+ putStrLn $ "=== " ++ xs ++ " ==="+ r <- qc p+ putStrLn ""+ return $ case r of+ Success { } -> True+ Failure { } -> False+ NoExpectedFailure { } -> False+ GaveUp { } -> False
+ src/Test/QuickCheck/Arbitrary.hs view
@@ -0,0 +1,1981 @@+-- | Type classes for random generation of values.+--+-- __Note__: the contents of this module are re-exported by+-- "Test.QuickCheck". You do not need to import it directly.+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleContexts #-}+#ifndef NO_GENERICS+{-# LANGUAGE DefaultSignatures, FlexibleContexts, TypeOperators #-}+{-# LANGUAGE FlexibleInstances, KindSignatures, ScopedTypeVariables #-}+{-# LANGUAGE MultiParamTypeClasses #-}+#if __GLASGOW_HASKELL__ >= 710+#define OVERLAPPING_ {-# OVERLAPPING #-}+#else+{-# LANGUAGE OverlappingInstances #-}+#define OVERLAPPING_+#endif+#endif+#ifndef NO_POLYKINDS+{-# LANGUAGE PolyKinds #-}+#endif+#ifndef NO_SAFE_HASKELL+{-# LANGUAGE Trustworthy #-}+#endif+#ifndef NO_NEWTYPE_DERIVING+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+#endif+module Test.QuickCheck.Arbitrary+ (+ -- * Arbitrary and CoArbitrary classes+ Arbitrary(..)+ , CoArbitrary(..)++ -- ** Unary and Binary classes+ , Arbitrary1(..)+ , arbitrary1+ , shrink1+ , Arbitrary2(..)+ , arbitrary2+ , shrink2++ -- ** Helper functions for implementing arbitrary+ , applyArbitrary2+ , applyArbitrary3+ , applyArbitrary4+ , 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+ , arbitraryBoundedRandom -- :: (Bounded a, Random a) => Gen a+ , arbitraryBoundedEnum -- :: (Bounded a, Enum a) => Gen a+ -- ** Generators for various kinds of character+ , arbitraryUnicodeChar -- :: Gen Char+ , arbitraryASCIIChar -- :: Gen Char+ , arbitraryPrintableChar -- :: Gen Char+ -- ** Helper functions for implementing shrink+#ifndef NO_GENERICS+ , RecursivelyShrink+ , GSubterms+ , 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]]+ , shrinkMap -- :: Arbitrary a -> (a -> b) -> (b -> a) -> b -> [b]+ , shrinkMapBy -- :: (a -> b) -> (b -> a) -> (a -> [a]) -> b -> [b]+ , shrinkIntegral -- :: Integral a => a -> [a]+ , shrinkRealFrac -- :: RealFrac a => a -> [a]+ , shrinkBoundedEnum -- :: (Bounded a, Enum a) => a -> [a]+ , shrinkDecimal -- :: RealFrac a => a -> [a]+ -- ** Helper functions for implementing coarbitrary+ , coarbitraryIntegral -- :: Integral a => a -> Gen b -> Gen b+ , coarbitraryReal -- :: Real a => a -> Gen b -> Gen b+ , coarbitraryShow -- :: Show a => a -> Gen b -> Gen b+ , coarbitraryEnum -- :: Enum a => a -> Gen b -> Gen b+ , (><)++ -- ** Generators which use arbitrary+ , vector -- :: Arbitrary a => Int -> Gen [a]+ , orderedList -- :: (Ord a, Arbitrary a) => Gen [a]+ , infiniteList -- :: Arbitrary a => Gen [a]+ )+ where++--------------------------------------------------------------------------+-- imports++import Control.Applicative+import Data.Foldable(toList)+#if MIN_VERSION_random(1,3,0)+import System.Random(Random, uniformByteArray)+#else+import System.Random(Random)+#endif+import Test.QuickCheck.Gen+import Test.QuickCheck.Random+import Test.QuickCheck.Gen.Unsafe+#if defined(__MHS__)+-- These two are not exported by Control.Applicative.+-- Why should they be? They are just bloat.+import Data.ZipList+import Control.WrappedMonad+#endif++import Data.Char+ ( ord+ , isLower+ , isUpper+ , toLower+ , isDigit+ , isSpace+ , isPrint+ , generalCategory+ , GeneralCategory(..)+ )++#ifndef NO_FIXED+import Data.Fixed+ ( Fixed+ , HasResolution+ )+#endif++import Data.Ratio+ ( Ratio+ , (%)+ , numerator+ , denominator+ )++import Data.Complex+ ( Complex((:+)) )++import Data.List+ ( sort+ , nub+ )+++import Data.Version (Version (..))++#if defined(MIN_VERSION_base)+import Numeric.Natural++import Data.List.NonEmpty (NonEmpty)+import qualified Data.List.NonEmpty as NonEmpty++import System.IO+ ( Newline(..)+ , NewlineMode(..)+ , SeekMode(..)+ , BufferMode(..)+ , TextEncoding+ , latin1, utf8, utf8_bom, utf16, utf16le, utf16be, utf32, utf32le, utf32be, localeEncoding, char8+ , IOMode(..)+ )+#endif++import Control.Monad+ ( liftM+ , liftM2+ , liftM3+ , liftM4+ , liftM5+ )++import Data.Int(Int8, Int16, Int32, Int64)+import Data.Word(Word, Word8, Word16, Word32, Word64)+import System.Exit (ExitCode(..))+import Foreign.C.Types++#ifndef NO_GENERICS+import GHC.Generics+#endif++import qualified Data.Set as Set+import qualified Data.IntSet as IntSet+#if MIN_VERSION_containers(0,5,0)+import qualified Data.Map.Strict as Map+import qualified Data.IntMap.Strict as IntMap+#else+import qualified Data.Map as Map+import qualified Data.IntMap as IntMap+#endif+import qualified Data.Sequence as Sequence+import qualified Data.Tree as Tree++import qualified Data.Monoid as Monoid+#if defined(MIN_VERSION_base)+import qualified Data.Semigroup as Semigroup+#endif++#ifndef NO_TRANSFORMERS+import Data.Functor.Identity+import Data.Functor.Constant+import Data.Functor.Compose+import Data.Functor.Product+#endif++#if defined(MIN_VERSION_base)+import qualified Data.Semigroup as Semigroup+import Data.Ord++import System.Console.GetOpt+ ( ArgDescr(..), ArgOrder(..), OptDescr(..) )++import Data.Functor.Contravariant++import Data.Array.Byte+import qualified GHC.Exts as Exts++#if MIN_VERSION_base(4,16,0)+import Data.Tuple+#endif+#endif++import Data.Bits+import Text.Printf++import Test.QuickCheck.Compat++--------------------------------------------------------------------------+-- ** class Arbitrary++-- | Random generation and shrinking of values.+--+-- QuickCheck provides @Arbitrary@ instances for most types in @base@,+-- except those which incur extra dependencies.+-- For a wider range of @Arbitrary@ instances see the+-- <http://hackage.haskell.org/package/quickcheck-instances quickcheck-instances>+-- package.+class Arbitrary a where+ -- | A generator for values of the given type.+ --+ -- It is worth spending time thinking about what sort of test data+ -- you want - good generators are often the difference between+ -- finding bugs and not finding them. You can use 'sample',+ -- 'Test.QuickCheck.label' and 'Test.QuickCheck.classify' to check the quality+ -- of your test data.+ --+ -- There is no generic @arbitrary@ implementation included because we don't+ -- know how to make a high-quality one. If you want one, consider using the+ -- <http://hackage.haskell.org/package/testing-feat testing-feat> or+ -- <http://hackage.haskell.org/package/generic-random generic-random> packages.+ --+ -- The <http://www.cse.chalmers.se/~rjmh/QuickCheck/manual.html QuickCheck manual>+ -- goes into detail on how to write good generators. Make sure to look at it,+ -- especially if your type is recursive!+ arbitrary :: Gen a++ -- | Produces a (possibly) empty list of all the possible+ -- immediate shrinks of the given value.+ --+ -- The default implementation returns the empty list, so will not try to+ -- shrink the value. If your data type has no special invariants, you can+ -- enable shrinking by defining @shrink = 'genericShrink'@, but by customising+ -- the behaviour of @shrink@ you can often get simpler counterexamples.+ --+ -- Most implementations of 'shrink' should try at least three things:+ --+ -- 1. Shrink a term to any of its immediate subterms.+ -- You can use 'subterms' to do this.+ --+ -- 2. Recursively apply 'shrink' to all immediate subterms.+ -- You can use 'recursivelyShrink' to do this.+ --+ -- 3. Type-specific shrinkings such as replacing a constructor by a+ -- simpler constructor.+ --+ -- For example, suppose we have the following implementation of binary trees:+ --+ -- > data Tree a = Nil | Branch a (Tree a) (Tree a)+ --+ -- We can then define 'shrink' as follows:+ --+ -- > shrink Nil = []+ -- > shrink (Branch x l r) =+ -- > -- shrink Branch to Nil+ -- > [Nil] +++ -- > -- shrink to non-Nil subterms+ -- > [t | t@Branch{} <- [l, r]] +++ -- > -- recursively shrink subterms+ -- > [Branch x' l' r' | (x', l', r') <- shrink (x, l, r)]+ --+ -- There are a couple of subtleties here:+ --+ -- * QuickCheck tries the shrinking candidates in the order they+ -- appear in the list, so we put more aggressive shrinking steps+ -- (such as replacing the whole tree by @Nil@) before smaller+ -- ones (such as recursively shrinking the subtrees).+ --+ -- * It is tempting to write the last line as+ -- @[Branch x' l' r' | x' <- shrink x, l' <- shrink l, r' <- shrink r]@+ -- but this is the /wrong thing/! It will force QuickCheck to shrink+ -- @x@, @l@ and @r@ in tandem, and shrinking will stop once /one/ of+ -- the three is fully shrunk.+ --+ -- There is a fair bit of boilerplate in the code above.+ -- We can avoid it with the help of some generic functions.+ -- The function 'genericShrink' tries shrinking a term to all of its+ -- subterms and, failing that, recursively shrinks the subterms.+ -- Using it, we can define 'shrink' as:+ --+ -- > shrink x = shrinkToNil x ++ genericShrink x+ -- > where+ -- > shrinkToNil Nil = []+ -- > shrinkToNil (Branch _ l r) = [Nil]+ --+ -- 'genericShrink' is a combination of 'subterms', which shrinks+ -- a term to any of its subterms, and 'recursivelyShrink', which shrinks+ -- all subterms of a term. These may be useful if you need a bit more+ -- control over shrinking than 'genericShrink' gives you.+ --+ -- A final gotcha: we cannot define 'shrink' as simply @'shrink' x = Nil:'genericShrink' x@+ -- as this shrinks @Nil@ to @Nil@, and shrinking will go into an+ -- infinite loop.+ --+ -- If all this leaves you bewildered, you might try @'shrink' = 'genericShrink'@ to begin with,+ -- 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 _ = []++-- | Lifting of the 'Arbitrary' class to unary type constructors.+class Arbitrary1 f where+ liftArbitrary :: Gen a -> Gen (f a)+ liftShrink :: (a -> [a]) -> f a -> [f a]+ liftShrink _ _ = []++arbitrary1 :: (Arbitrary1 f, Arbitrary a) => Gen (f a)+arbitrary1 = liftArbitrary arbitrary++shrink1 :: (Arbitrary1 f, Arbitrary a) => f a -> [f a]+shrink1 = liftShrink shrink++-- | Lifting of the 'Arbitrary' class to binary type constructors.+class Arbitrary2 f where+ liftArbitrary2 :: Gen a -> Gen b -> Gen (f a b)+ liftShrink2 :: (a -> [a]) -> (b -> [b]) -> f a b -> [f a b]+ liftShrink2 _ _ _ = []++arbitrary2 :: (Arbitrary2 f, Arbitrary a, Arbitrary b) => Gen (f a b)+arbitrary2 = liftArbitrary2 arbitrary arbitrary++shrink2 :: (Arbitrary2 f, Arbitrary a, Arbitrary b) => f a b -> [f a b]+shrink2 = liftShrink2 shrink shrink++#ifndef NO_GENERICS+-- | Shrink a term to any of its immediate subterms,+-- and also recursively shrink all subterms.+genericShrink :: (Generic a, RecursivelyShrink (Rep a), GSubterms (Rep a) a) => a -> [a]+genericShrink x = subterms x ++ recursivelyShrink x++-- | Recursively shrink all immediate subterms.+recursivelyShrink :: (Generic a, RecursivelyShrink (Rep a)) => a -> [a]+recursivelyShrink = map to . grecursivelyShrink . from++class RecursivelyShrink f where+ grecursivelyShrink :: f a -> [f a]++instance (RecursivelyShrink f, RecursivelyShrink g) => RecursivelyShrink (f :*: g) where+ grecursivelyShrink (x :*: y) =+ [x' :*: y | x' <- grecursivelyShrink x] +++ [x :*: y' | y' <- grecursivelyShrink y]++instance (RecursivelyShrink f, RecursivelyShrink g) => RecursivelyShrink (f :+: g) where+ grecursivelyShrink (L1 x) = map L1 (grecursivelyShrink x)+ grecursivelyShrink (R1 x) = map R1 (grecursivelyShrink x)++instance RecursivelyShrink f => RecursivelyShrink (M1 i c f) where+ grecursivelyShrink (M1 x) = map M1 (grecursivelyShrink x)++instance Arbitrary a => RecursivelyShrink (K1 i a) where+ grecursivelyShrink (K1 x) = map K1 (shrink x)++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, GSubterms (Rep a) a) => a -> [a]+subterms = gSubterms . from+++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 GSubterms V1 a where+ -- The empty type can't be shrunk to anything.+ gSubterms _ = []++instance GSubterms U1 a where+ gSubterms U1 = []++instance (GSubtermsIncl f a, GSubtermsIncl g a) => GSubterms (f :*: g) a where+ gSubterms (l :*: r) = gSubtermsIncl l ++ gSubtermsIncl r++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 OVERLAPPING_ GSubtermsIncl (K1 i a) a where+ gSubtermsIncl (K1 x) = [x]++instance GSubtermsIncl (K1 i a) b where+ gSubtermsIncl (K1 _) = []++#endif++-- instances++instance (CoArbitrary a) => Arbitrary1 ((->) a) where+ liftArbitrary arbB = promote (`coarbitrary` arbB)++instance (CoArbitrary a, Arbitrary b) => Arbitrary (a -> b) where+ arbitrary = arbitrary1++instance Arbitrary () where+ arbitrary = return ()++instance Arbitrary Bool where+ arbitrary = chooseEnum (False,True)+ shrink True = [False]+ shrink False = []++instance Arbitrary Ordering where+ arbitrary = elements [LT, EQ, GT]+ shrink GT = [EQ, LT]+ shrink LT = [EQ]+ shrink EQ = []++instance Arbitrary1 Maybe where+ liftArbitrary arb = frequency [(1, return Nothing), (3, liftM Just arb)]++ liftShrink shr (Just x) = Nothing : [ Just x' | x' <- shr x ]+ liftShrink _ Nothing = []++instance Arbitrary a => Arbitrary (Maybe a) where+ arbitrary = arbitrary1+ shrink = shrink1++instance Arbitrary2 Either where+ liftArbitrary2 arbA arbB = oneof [liftM Left arbA, liftM Right arbB]++ liftShrink2 shrA _ (Left x) = [ Left x' | x' <- shrA x ]+ liftShrink2 _ shrB (Right y) = [ Right y' | y' <- shrB y ]++instance Arbitrary a => Arbitrary1 (Either a) where+ liftArbitrary = liftArbitrary2 arbitrary+ liftShrink = liftShrink2 shrink++instance (Arbitrary a, Arbitrary b) => Arbitrary (Either a b) where+ arbitrary = arbitrary2+ shrink = shrink2++instance Arbitrary1 [] where+ liftArbitrary = listOf+ liftShrink = shrinkList++instance Arbitrary a => Arbitrary [a] where+ arbitrary = arbitrary1+ shrink = shrink1++-- | Shrink a list of values given a shrinking function for individual values.+shrinkList :: (a -> [a]) -> [a] -> [[a]]+shrinkList shr xs = concat [ removes k n xs | k <- takeWhile (>0) (iterate (`div`2) n) ]+ ++ shrinkOne xs+ where+ n = length xs++ shrinkOne [] = []+ shrinkOne (x:xs) = [ x':xs | x' <- shr x ]+ ++ [ x:xs' | xs' <- shrinkOne xs ]++ removes k n xs+ | k > n = []+ | null xs2 = [[]]+ | otherwise = xs2 : map (xs1 ++) (removes k (n-k) xs2)+ where+ xs1 = take k xs+ xs2 = drop k xs++#if defined(MIN_VERSION_base)+instance Arbitrary1 NonEmpty where+ liftArbitrary arb = NonEmpty.fromList <$> listOf1 arb+ liftShrink shr xs = [ NonEmpty.fromList xs' | xs' <- liftShrink shr (NonEmpty.toList xs), not (null xs') ]++instance Arbitrary a => Arbitrary (NonEmpty a) where+ arbitrary = arbitrary1+ shrink = shrink1+#endif++instance Integral a => Arbitrary (Ratio a) where+ arbitrary = sized $ \ n -> do+ denom <- chooseInt (1, max 1 n)+ let lb | isNonNegativeType fromI = 0+ | otherwise = (-n*denom)+ -- NOTE: this is a trick to make sure we get around lack of scoped type+ -- variables by pinning the result-type of fromIntegral.+ fromI = fromIntegral+ numer <- chooseInt (lb, n*denom)+ pure $ fromI numer % fromI denom+ shrink = shrinkRealFrac+++#if defined(MIN_VERSION_base)+instance Arbitrary a => Arbitrary (Complex a) where+#else+instance (RealFloat a, Arbitrary a) => Arbitrary (Complex a) where+#endif+ arbitrary = liftM2 (:+) arbitrary arbitrary+ shrink (x :+ y) = [ x' :+ y | x' <- shrink x ] +++ [ x :+ y' | y' <- shrink y ]++#ifndef NO_FIXED+instance HasResolution a => Arbitrary (Fixed a) where+ arbitrary = arbitrarySizedFractional+ shrink = shrinkDecimal+#endif++instance Arbitrary2 (,) where+ liftArbitrary2 = liftM2 (,)+ liftShrink2 shrA shrB (x, y) =+ [ (x', y) | x' <- shrA x ]+ ++ [ (x, y') | y' <- shrB y ]++instance (Arbitrary a) => Arbitrary1 ((,) a) where+ liftArbitrary = liftArbitrary2 arbitrary+ liftShrink = liftShrink2 shrink++instance (Arbitrary a, Arbitrary b) => Arbitrary (a,b) where+ arbitrary = arbitrary2+ shrink = shrink2++instance (Arbitrary a, Arbitrary b, Arbitrary c)+ => Arbitrary (a,b,c)+ where+ arbitrary = liftM3 (,,) arbitrary arbitrary arbitrary++ shrink (x, y, z) =+ [ (x', y', z')+ | (x', (y', z')) <- shrink (x, (y, z)) ]++instance (Arbitrary a, Arbitrary b, Arbitrary c, Arbitrary d)+ => Arbitrary (a,b,c,d)+ where+ arbitrary = liftM4 (,,,) arbitrary arbitrary arbitrary arbitrary++ shrink (w, x, y, z) =+ [ (w', x', y', z')+ | (w', (x', (y', z'))) <- shrink (w, (x, (y, z))) ]++instance (Arbitrary a, Arbitrary b, Arbitrary c, Arbitrary d, Arbitrary e)+ => Arbitrary (a,b,c,d,e)+ where+ arbitrary = liftM5 (,,,,) arbitrary arbitrary arbitrary arbitrary arbitrary++ shrink (v, w, x, y, z) =+ [ (v', w', x', y', z')+ | (v', (w', (x', (y', z')))) <- shrink (v, (w, (x, (y, z)))) ]++instance ( Arbitrary a, Arbitrary b, Arbitrary c, Arbitrary d, Arbitrary e+ , Arbitrary f+ )+ => Arbitrary (a,b,c,d,e,f)+ where+ arbitrary = return (,,,,,)+ <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary+ <*> arbitrary <*> arbitrary++ shrink (u, v, w, x, y, z) =+ [ (u', v', w', x', y', z')+ | (u', (v', (w', (x', (y', z'))))) <- shrink (u, (v, (w, (x, (y, z))))) ]++instance ( Arbitrary a, Arbitrary b, Arbitrary c, Arbitrary d, Arbitrary e+ , Arbitrary f, Arbitrary g+ )+ => Arbitrary (a,b,c,d,e,f,g)+ where+ arbitrary = return (,,,,,,)+ <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary+ <*> arbitrary <*> arbitrary <*> arbitrary++ shrink (t, u, v, w, x, y, z) =+ [ (t', u', v', w', x', y', z')+ | (t', (u', (v', (w', (x', (y', z')))))) <- shrink (t, (u, (v, (w, (x, (y, z)))))) ]++instance ( Arbitrary a, Arbitrary b, Arbitrary c, Arbitrary d, Arbitrary e+ , Arbitrary f, Arbitrary g, Arbitrary h+ )+ => Arbitrary (a,b,c,d,e,f,g,h)+ where+ arbitrary = return (,,,,,,,)+ <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary+ <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary++ shrink (s, t, u, v, w, x, y, z) =+ [ (s', t', u', v', w', x', y', z')+ | (s', (t', (u', (v', (w', (x', (y', z')))))))+ <- shrink (s, (t, (u, (v, (w, (x, (y, z))))))) ]++instance ( Arbitrary a, Arbitrary b, Arbitrary c, Arbitrary d, Arbitrary e+ , Arbitrary f, Arbitrary g, Arbitrary h, Arbitrary i+ )+ => Arbitrary (a,b,c,d,e,f,g,h,i)+ where+ arbitrary = return (,,,,,,,,)+ <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary+ <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary+ <*> arbitrary++ shrink (r, s, t, u, v, w, x, y, z) =+ [ (r', s', t', u', v', w', x', y', z')+ | (r', (s', (t', (u', (v', (w', (x', (y', z'))))))))+ <- shrink (r, (s, (t, (u, (v, (w, (x, (y, z)))))))) ]++instance ( Arbitrary a, Arbitrary b, Arbitrary c, Arbitrary d, Arbitrary e+ , Arbitrary f, Arbitrary g, Arbitrary h, Arbitrary i, Arbitrary j+ )+ => Arbitrary (a,b,c,d,e,f,g,h,i,j)+ where+ arbitrary = return (,,,,,,,,,)+ <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary+ <*> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary+ <*> arbitrary <*> arbitrary++ shrink (q, r, s, t, u, v, w, x, y, z) =+ [ (q', r', s', t', u', v', w', x', y', z')+ | (q', (r', (s', (t', (u', (v', (w', (x', (y', z')))))))))+ <- shrink (q, (r, (s, (t, (u, (v, (w, (x, (y, z))))))))) ]++-- typical instance for primitive (numerical) types++instance Arbitrary Integer where+ arbitrary = arbitrarySizedIntegral+ shrink = shrinkIntegral++#if defined(MIN_VERSION_base)+instance Arbitrary Natural where+ arbitrary = arbitrarySizedNatural+ shrink = shrinkIntegral+#endif++instance Arbitrary Int where+ arbitrary = arbitrarySizedIntegral+ shrink = shrinkIntegral++instance Arbitrary Int8 where+ arbitrary = arbitrarySizedBoundedIntegral+ shrink = shrinkIntegral++instance Arbitrary Int16 where+ arbitrary = arbitrarySizedBoundedIntegral+ shrink = shrinkIntegral++instance Arbitrary Int32 where+ arbitrary = arbitrarySizedBoundedIntegral+ shrink = shrinkIntegral++instance Arbitrary Int64 where+ arbitrary = arbitrarySizedBoundedIntegral+ shrink = shrinkIntegral++instance Arbitrary Word where+ arbitrary = arbitrarySizedNatural+ shrink = shrinkIntegral++instance Arbitrary Word8 where+ arbitrary = arbitrarySizedBoundedIntegral+ shrink = shrinkIntegral++instance Arbitrary Word16 where+ arbitrary = arbitrarySizedBoundedIntegral+ shrink = shrinkIntegral++instance Arbitrary Word32 where+ arbitrary = arbitrarySizedBoundedIntegral+ shrink = shrinkIntegral++instance Arbitrary Word64 where+ arbitrary = arbitrarySizedBoundedIntegral+ shrink = shrinkIntegral++instance Arbitrary Char where+ arbitrary =+ frequency+ [(3, arbitraryASCIIChar),+ (1, arbitraryUnicodeChar)]++ shrink c = filter (<. c) $ nub+ $ ['a','b','c']+ ++ [ toLower c | isUpper c ]+ ++ ['A','B','C']+ ++ ['1','2','3']+ ++ [' ','\n']+ where+ a <. b = stamp a < stamp b+ stamp a = ( (not (isLower a)+ , not (isUpper a)+ , not (isDigit a))+ , (not (a==' ')+ , not (isSpace a)+ , a)+ )++instance Arbitrary Float where+ arbitrary = oneof+ -- generate 0..1 numbers with full precision+ [ genFloat+ -- generate integral numbers+ , fromIntegral <$> (arbitrary :: Gen Int)+ -- generate fractions with small denominators+ , smallDenominators+ -- uniform -size..size with with denominators ~ size+ , uniform+ -- and uniform -size..size with higher precision+ , arbitrarySizedFractional+ ]+ where+ smallDenominators = sized $ \n -> do+ i <- chooseInt (0, min n 256)+ pure (fromRational (streamNth i rationalUniverse))++ uniform = sized $ \n -> do+ let n' = toInteger n+ b <- chooseInteger (1, max 1 n')+ a <- chooseInteger ((-n') * b, n' * b)+ return (fromRational (a % b))++ shrink = shrinkDecimal++instance Arbitrary Double where+ arbitrary = oneof+ -- generate 0..1 numbers with full precision+ [ genDouble+ -- generate integral numbers+ , fromIntegral <$> (arbitrary :: Gen Int)+ -- generate fractions with small denominators+ , smallDenominators+ -- uniform -size..size with with denominators ~ size+ , uniform+ -- and uniform -size..size with higher precision+ , arbitrarySizedFractional+ ]+ where+ smallDenominators = sized $ \n -> do+ i <- chooseInt (0, min n 256)+ pure (fromRational (streamNth i rationalUniverse))++ uniform = sized $ \n -> do+ let n' = toInteger n+ b <- chooseInteger (1, max 1 n')+ a <- chooseInteger ((-n') * b, n' * b)+ return (fromRational (a % b))++ shrink = shrinkDecimal++instance Arbitrary CChar where+ arbitrary = arbitrarySizedBoundedIntegral+ shrink = shrinkIntegral++instance Arbitrary CSChar where+ arbitrary = arbitrarySizedBoundedIntegral+ shrink = shrinkIntegral++instance Arbitrary CUChar where+ arbitrary = arbitrarySizedBoundedIntegral+ shrink = shrinkIntegral++instance Arbitrary CShort where+ arbitrary = arbitrarySizedBoundedIntegral+ shrink = shrinkIntegral++instance Arbitrary CUShort where+ arbitrary = arbitrarySizedBoundedIntegral+ shrink = shrinkIntegral++instance Arbitrary CInt where+ arbitrary = arbitrarySizedBoundedIntegral+ shrink = shrinkIntegral++instance Arbitrary CUInt where+ arbitrary = arbitrarySizedBoundedIntegral+ shrink = shrinkIntegral++instance Arbitrary CLong where+ arbitrary = arbitrarySizedBoundedIntegral+ shrink = shrinkIntegral++instance Arbitrary CULong where+ arbitrary = arbitrarySizedBoundedIntegral+ shrink = shrinkIntegral++instance Arbitrary CPtrdiff where+ arbitrary = arbitrarySizedBoundedIntegral+ shrink = shrinkIntegral++instance Arbitrary CSize where+ arbitrary = arbitrarySizedBoundedIntegral+ shrink = shrinkIntegral++instance Arbitrary CWchar where+ arbitrary = arbitrarySizedBoundedIntegral+ shrink = shrinkIntegral++instance Arbitrary CSigAtomic where+ arbitrary = arbitrarySizedBoundedIntegral+ shrink = shrinkIntegral++instance Arbitrary CLLong where+ arbitrary = arbitrarySizedBoundedIntegral+ shrink = shrinkIntegral++instance Arbitrary CULLong where+ arbitrary = arbitrarySizedBoundedIntegral+ shrink = shrinkIntegral++instance Arbitrary CIntPtr where+ arbitrary = arbitrarySizedBoundedIntegral+ shrink = shrinkIntegral++instance Arbitrary CUIntPtr where+ arbitrary = arbitrarySizedBoundedIntegral+ shrink = shrinkIntegral++instance Arbitrary CIntMax where+ arbitrary = arbitrarySizedBoundedIntegral+ shrink = shrinkIntegral++instance Arbitrary CUIntMax where+ arbitrary = arbitrarySizedBoundedIntegral+ shrink = shrinkIntegral++#ifndef NO_CTYPES_CONSTRUCTORS+-- The following four types have no Bounded instance,+-- so we fake it by discovering the bounds at runtime.+instance Arbitrary CClock where+ arbitrary = fmap CClock arbitrary+ shrink (CClock x) = map CClock (shrink x)++instance Arbitrary CTime where+ arbitrary = fmap CTime arbitrary+ shrink (CTime x) = map CTime (shrink x)++#ifndef NO_FOREIGN_C_USECONDS+instance Arbitrary CUSeconds where+ arbitrary = fmap CUSeconds arbitrary+ shrink (CUSeconds x) = map CUSeconds (shrink x)++instance Arbitrary CSUSeconds where+ arbitrary = fmap CSUSeconds arbitrary+ shrink (CSUSeconds x) = map CSUSeconds (shrink x)+#endif+#endif++instance Arbitrary CFloat where+ arbitrary = arbitrarySizedFractional+ shrink = shrinkDecimal++instance Arbitrary CDouble where+ arbitrary = arbitrarySizedFractional+ shrink = shrinkDecimal++-- Arbitrary instances for container types+-- | WARNING: Users working on the internals of the @Set@ type via e.g. @Data.Set.Internal@+-- should be aware that this instance aims to give a good representation of @Set a@+-- as mathematical sets but *does not* aim to provide a varied distribution over the+-- underlying representation.+instance (Ord a, Arbitrary a) => Arbitrary (Set.Set a) where+ arbitrary = fmap Set.fromList arbitrary+ shrink = map Set.fromList . shrink . Set.toList+instance (Ord k, Arbitrary k) => Arbitrary1 (Map.Map k) where+ liftArbitrary = fmap Map.fromList . liftArbitrary . liftArbitrary+ liftShrink shr = map Map.fromList . liftShrink (liftShrink shr) . Map.toList+-- | WARNING: The same warning as for @Arbitrary (Set a)@ applies here.+instance (Ord k, Arbitrary k, Arbitrary v) => Arbitrary (Map.Map k v) where+ arbitrary = arbitrary1+ shrink = shrink1+-- | WARNING: The same warning as for @Arbitrary (Set a)@ applies here.+instance Arbitrary IntSet.IntSet where+ arbitrary = fmap IntSet.fromList arbitrary+ shrink = map IntSet.fromList . shrink . IntSet.toList+-- | WARNING: The same warning as for @Arbitrary (Set a)@ applies here.+instance Arbitrary1 IntMap.IntMap where+ liftArbitrary = fmap IntMap.fromList . liftArbitrary . liftArbitrary+ liftShrink shr = map IntMap.fromList . liftShrink (liftShrink shr) . IntMap.toList+-- | WARNING: The same warning as for @Arbitrary (Set a)@ applies here.+instance Arbitrary a => Arbitrary (IntMap.IntMap a) where+ arbitrary = arbitrary1+ shrink = shrink1+instance Arbitrary1 Sequence.Seq where+ liftArbitrary = fmap Sequence.fromList . liftArbitrary+ liftShrink shr = map Sequence.fromList . liftShrink shr . toList+-- | WARNING: The same warning as for @Arbitrary (Set a)@ applies here.+instance Arbitrary a => Arbitrary (Sequence.Seq a) where+ arbitrary = arbitrary1+ shrink = shrink1+instance Arbitrary1 Tree.Tree where+ liftArbitrary arb = sized $ \n -> do+ k <- chooseInt (0, n)+ go k+ where+ go n = do -- n is the size of the trees.+ value <- arb+ pars <- arbPartition (n - 1) -- can go negative!+ forest <- mapM go pars+ return $ Tree.Node value forest++ arbPartition :: Int -> Gen [Int]+ arbPartition k = case compare k 1 of+ LT -> pure []+ EQ -> pure [1]+ GT -> do+ first <- chooseInt (1, k)+ rest <- arbPartition $ k - first+ shuffle (first : rest)++ liftShrink shr = go+ where+ go (Tree.Node val forest) = forest +++ [ Tree.Node e fs+ | (e, fs) <- liftShrink2 shr (liftShrink go) (val, forest)+ ]+instance Arbitrary a => Arbitrary (Tree.Tree a) where+ arbitrary = arbitrary1+ shrink = shrink1++-- Arbitrary instance for Ziplist+instance Arbitrary1 ZipList where+ liftArbitrary = fmap ZipList . liftArbitrary+ liftShrink shr = map ZipList . liftShrink shr . getZipList+instance Arbitrary a => Arbitrary (ZipList a) where+ arbitrary = arbitrary1+ shrink = shrink1++#ifndef NO_TRANSFORMERS+-- Arbitrary instance for transformers' Functors+instance Arbitrary1 Identity where+ liftArbitrary = fmap Identity+ liftShrink shr = map Identity . shr . runIdentity+instance Arbitrary a => Arbitrary (Identity a) where+ arbitrary = arbitrary1+ shrink = shrink1++instance Arbitrary2 Constant where+ liftArbitrary2 arbA _ = fmap Constant arbA+ liftShrink2 shrA _ = fmap Constant . shrA . getConstant+instance Arbitrary a => Arbitrary1 (Constant a) where+ liftArbitrary = liftArbitrary2 arbitrary+ liftShrink = liftShrink2 shrink+-- Have to be defined explicitly, as Constant is kind polymorphic+instance Arbitrary a => Arbitrary (Constant a b) where+ arbitrary = fmap Constant arbitrary+ shrink = map Constant . shrink . getConstant++instance (Arbitrary1 f, Arbitrary1 g) => Arbitrary1 (Product f g) where+ liftArbitrary arb = liftM2 Pair (liftArbitrary arb) (liftArbitrary arb)+ liftShrink shr (Pair f g) =+ [ Pair f' g | f' <- liftShrink shr f ] +++ [ Pair f g' | g' <- liftShrink shr g ]+instance (Arbitrary1 f, Arbitrary1 g, Arbitrary a) => Arbitrary (Product f g a) where+ arbitrary = arbitrary1+ shrink = shrink1++instance (Arbitrary1 f, Arbitrary1 g) => Arbitrary1 (Compose f g) where+ liftArbitrary = fmap Compose . liftArbitrary . liftArbitrary+ liftShrink shr = map Compose . liftShrink (liftShrink shr) . getCompose+instance (Arbitrary1 f, Arbitrary1 g, Arbitrary a) => Arbitrary (Compose f g a) where+ arbitrary = arbitrary1+ shrink = shrink1+#endif++-- Arbitrary instance for Const+instance Arbitrary2 Const where+ liftArbitrary2 arbA _ = fmap Const arbA+ liftShrink2 shrA _ = fmap Const . shrA . getConst+instance Arbitrary a => Arbitrary1 (Const a) where+ liftArbitrary = liftArbitrary2 arbitrary+ liftShrink = liftShrink2 shrink+-- Have to be defined explicitly, as Const is kind polymorphic+instance Arbitrary a => Arbitrary (Const a b) where+ arbitrary = fmap Const arbitrary+ shrink = map Const . shrink . getConst++instance Arbitrary (m a) => Arbitrary (WrappedMonad m a) where+ arbitrary = WrapMonad <$> arbitrary+ shrink (WrapMonad a) = map WrapMonad (shrink a)++instance Arbitrary (a b c) => Arbitrary (WrappedArrow a b c) where+ arbitrary = WrapArrow <$> arbitrary+ shrink (WrapArrow a) = map WrapArrow (shrink a)++-- Arbitrary instances for Monoid+instance Arbitrary a => Arbitrary (Monoid.Dual a) where+ arbitrary = fmap Monoid.Dual arbitrary+ shrink = map Monoid.Dual . shrink . Monoid.getDual++instance (Arbitrary a, CoArbitrary a) => Arbitrary (Monoid.Endo a) where+ arbitrary = fmap Monoid.Endo arbitrary+ shrink = map Monoid.Endo . shrink . Monoid.appEndo++instance Arbitrary Monoid.All where+ arbitrary = fmap Monoid.All arbitrary+ shrink = map Monoid.All . shrink . Monoid.getAll++instance Arbitrary Monoid.Any where+ arbitrary = fmap Monoid.Any arbitrary+ shrink = map Monoid.Any . shrink . Monoid.getAny++instance Arbitrary a => Arbitrary (Monoid.Sum a) where+ arbitrary = fmap Monoid.Sum arbitrary+ shrink = map Monoid.Sum . shrink . Monoid.getSum++instance Arbitrary a => Arbitrary (Monoid.Product a) where+ arbitrary = fmap Monoid.Product arbitrary+ shrink = map Monoid.Product . shrink . Monoid.getProduct++#if defined(MIN_VERSION_base)+instance Arbitrary a => Arbitrary (Monoid.First a) where+ arbitrary = fmap Monoid.First arbitrary+ shrink = map Monoid.First . shrink . Monoid.getFirst++instance Arbitrary a => Arbitrary (Monoid.Last a) where+ arbitrary = fmap Monoid.Last arbitrary+ shrink = map Monoid.Last . shrink . Monoid.getLast++instance Arbitrary (f a) => Arbitrary (Monoid.Alt f a) where+ arbitrary = fmap Monoid.Alt arbitrary+ shrink = map Monoid.Alt . shrink . Monoid.getAlt++instance Arbitrary a => Arbitrary (Semigroup.Min a) where+ arbitrary = fmap Semigroup.Min arbitrary+ shrink = map Semigroup.Min . shrink . Semigroup.getMin++instance Arbitrary a => Arbitrary (Semigroup.Max a) where+ arbitrary = fmap Semigroup.Max arbitrary+ shrink = map Semigroup.Max . shrink . Semigroup.getMax++instance Arbitrary a => Arbitrary (Semigroup.First a) where+ arbitrary = fmap Semigroup.First arbitrary+ shrink = map Semigroup.First . shrink . Semigroup.getFirst++instance Arbitrary a => Arbitrary (Semigroup.Last a) where+ arbitrary = fmap Semigroup.Last arbitrary+ shrink = map Semigroup.Last . shrink . Semigroup.getLast++instance (Arbitrary a, Arbitrary b) => Arbitrary (Semigroup.Arg a b) where+ arbitrary = Semigroup.Arg <$> arbitrary <*> arbitrary+ shrink (Semigroup.Arg a b) = uncurry Semigroup.Arg <$> shrink (a, b)++instance Arbitrary a => Arbitrary (Semigroup.WrappedMonoid a) where+ arbitrary = Semigroup.WrapMonoid <$> arbitrary+ shrink = map Semigroup.WrapMonoid . shrink . Semigroup.unwrapMonoid++#if !MIN_VERSION_base(4,15,0)+instance Arbitrary a => Arbitrary (Semigroup.Option a) where+ arbitrary = Semigroup.Option <$> arbitrary+ shrink = map Semigroup.Option . shrink . Semigroup.getOption++instance CoArbitrary a => CoArbitrary (Semigroup.Option a) where+ coarbitrary = coarbitrary . Semigroup.getOption+#endif++#if MIN_VERSION_base(4,16,0)+instance Arbitrary a => Arbitrary (Iff a) where+ arbitrary = Iff <$> arbitrary+ shrink = map Iff . shrink . getIff++instance Arbitrary a => Arbitrary (Ior a) where+ arbitrary = Ior <$> arbitrary+ shrink = map Ior . shrink . getIor++instance Arbitrary a => Arbitrary (Xor a) where+ arbitrary = Xor <$> arbitrary+ shrink = map Xor . shrink . getXor++instance Arbitrary a => Arbitrary (And a) where+ arbitrary = And <$> arbitrary+ shrink = map And . shrink . getAnd++instance CoArbitrary a => CoArbitrary (And a) where+ coarbitrary = coarbitrary . getAnd++instance CoArbitrary a => CoArbitrary (Iff a) where+ coarbitrary = coarbitrary . getIff++instance CoArbitrary a => CoArbitrary (Ior a) where+ coarbitrary = coarbitrary . getIor++instance CoArbitrary a => CoArbitrary (Xor a) where+ coarbitrary = coarbitrary . getXor+#endif++#if !defined(__MHS__)+instance Arbitrary ByteArray where+#if MIN_VERSION_random(1,3,0)+ arbitrary = do+ pin <- arbitrary+ len <- abs <$> arbitrary+ MkGen $ \ qcGen _ -> fst $ uniformByteArray pin len qcGen+#else+ arbitrary = Exts.fromList <$> arbitrary+#endif+ shrink = map Exts.fromList . shrink . Exts.toList++instance CoArbitrary ByteArray where+ coarbitrary = coarbitrary . Exts.toList++-- MicroHs does not have Exts.fromList+#endif /* !defined(__MHS__) */++#if MIN_VERSION_base(4,16,0)++instance Arbitrary a => Arbitrary (Solo a) where+ arbitrary = mkSolo <$> arbitrary+ shrink = map mkSolo . shrink . getSolo++instance CoArbitrary a => CoArbitrary (Solo a) where+ coarbitrary = coarbitrary . getSolo++#endif++instance Arbitrary a => Arbitrary (Down a) where+ arbitrary = fmap Down arbitrary+ shrink = map Down . shrink . getDown++instance CoArbitrary a => CoArbitrary (Down a) where+ coarbitrary = coarbitrary . getDown++#endif++#ifdef __GLASGOW_HASKELL__++instance Arbitrary a => Arbitrary (ArgDescr a) where+ arbitrary = oneof [ NoArg <$> arbitrary+ , ReqArg <$> arbitrary <*> arbitrary+ , OptArg <$> arbitrary <*> arbitrary+ ]++ shrink (NoArg i) = [ NoArg i' | i' <- shrink i ]+ shrink (ReqArg a1 a2) = [ ReqArg a1' a2 | a1' <- shrink a1 ] +++ [ ReqArg a1 a2' | a2' <- shrink a2 ]+ shrink (OptArg a1 a2) = [ OptArg a1' a2 | a1' <- shrink a1 ] +++ [ OptArg a1 a2' | a2' <- shrink a2 ]++instance Arbitrary a => Arbitrary (ArgOrder a) where+ arbitrary = oneof [ return RequireOrder+ , return Permute+ , ReturnInOrder <$> arbitrary+ ]++ shrink RequireOrder = []+ shrink Permute = []+ shrink (ReturnInOrder a) = [ ReturnInOrder a' | a' <- shrink a ]++instance Arbitrary a => Arbitrary (OptDescr a) where+ arbitrary = Option+ <$> arbitrary+ <*> arbitrary+ <*> arbitrary+ <*> arbitrary++ shrink (Option a b c d) = [ Option a' b c d | a' <- shrink a ] +++ [ Option a b' c d | b' <- shrink b ] +++ [ Option a b c' d | c' <- shrink c ] +++ [ Option a b c d' | d' <- shrink d ]++-- Data.Functor.Contravariant++-- can maybe use Arbitrary1/2 for these+instance CoArbitrary a => Arbitrary (Predicate a) where+ arbitrary = Predicate <$> arbitrary++ shrink (Predicate p) = [ Predicate p' | p' <- shrink p ]++instance (Arbitrary a, CoArbitrary b) => Arbitrary (Op a b) where+ arbitrary = Op <$> arbitrary++ shrink (Op f) = [ Op f' | f' <- shrink f ]++instance CoArbitrary a => Arbitrary (Equivalence a) where+ arbitrary = do+ Comparison cmp <- arbitrary+ return $ Equivalence (\x y -> cmp x y == EQ)++instance CoArbitrary a => Arbitrary (Comparison a) where+ arbitrary = do+ Comparison . comparing <$> (liftArbitrary arbitrary :: Gen (a -> Integer))++#endif++-- | Generates 'Version' with non-empty non-negative @versionBranch@, and empty @versionTags@+instance Arbitrary Version where+ arbitrary = sized $ \n ->+ do k <- chooseInt (0, log2 n)+ xs <- vectorOf (k+1) arbitrarySizedNatural+ return (Version xs [])+ where+ log2 :: Int -> Int+ log2 n | n <= 1 = 0+ | otherwise = 1 + log2 (n `div` 2)++ shrink (Version xs _) =+ [ Version xs' []+ | xs' <- shrink xs+ , length xs' > 0+ , all (>=0) xs'+ ]++instance Arbitrary QCGen where+ arbitrary = MkGen (\g _ -> g)++instance Arbitrary ExitCode where+ arbitrary = frequency [(1, return ExitSuccess), (3, liftM ExitFailure arbitrary)]++ shrink (ExitFailure x) = ExitSuccess : [ ExitFailure x' | x' <- shrink x ]+ shrink _ = []++#if defined(MIN_VERSION_base)+instance Arbitrary Newline where+ arbitrary = elements [LF, CRLF]++ -- The behavior of code for LF is generally simpler than for CRLF+ -- See the documentation for this type, which states that Haskell+ -- Internally always assumes newlines are \n and this type represents+ -- how to translate that to and from the outside world, where LF means+ -- no translation.+ shrink LF = []+ shrink CRLF = [LF]++instance Arbitrary NewlineMode where+ arbitrary = NewlineMode <$> arbitrary <*> arbitrary++ shrink (NewlineMode inNL outNL) = [NewlineMode inNL' outNL' | (inNL', outNL') <- shrink (inNL, outNL)]++instance Arbitrary GeneralCategory where+ arbitrary = arbitraryBoundedEnum+ shrink = shrinkBoundedEnum++instance Arbitrary SeekMode where+ arbitrary = elements [ AbsoluteSeek, RelativeSeek, SeekFromEnd ]+ shrink x = takeWhile (x /=) [ AbsoluteSeek, RelativeSeek, SeekFromEnd ]++instance Arbitrary TextEncoding where+ arbitrary = elements [ latin1, utf8, utf8_bom, utf16, utf16le, utf16be, utf32, utf32le, utf32be, localeEncoding, char8 ]++instance Arbitrary BufferMode where+ arbitrary = oneof [ pure NoBuffering+ , pure LineBuffering+ , pure $ BlockBuffering Nothing+ , BlockBuffering . Just . (+1) . fromIntegral <$> (arbitrary :: Gen Natural)+ ]+ shrink NoBuffering = []+ shrink LineBuffering = [ NoBuffering ]+ shrink (BlockBuffering m) = [ NoBuffering, LineBuffering ] ++ map BlockBuffering (filter (maybe True (>0)) $ shrink m)++instance Arbitrary IOMode where+ arbitrary = elements [ReadMode, WriteMode, AppendMode, ReadWriteMode]+ shrink x = takeWhile (/=x) [ReadMode, WriteMode, AppendMode, ReadWriteMode]++instance Arbitrary FormatSign where+ arbitrary = elements [SignPlus, SignSpace]+ shrink SignPlus = []+ shrink SignSpace = [SignPlus]++instance Arbitrary FormatAdjustment where+ arbitrary = elements [LeftAdjust, ZeroPad]+ shrink LeftAdjust = []+ shrink ZeroPad = [LeftAdjust]++instance Arbitrary FormatParse where+ arbitrary = FormatParse <$> arbitrary <*> arbitrary <*> arbitrary+ shrink (FormatParse a b c) = [ FormatParse a' b' c' | (a', b', c') <- shrink (a, b, c) ]++instance Arbitrary FieldFormat where+ arbitrary = FieldFormat <$> arbitrary+ <*> arbitrary+ <*> arbitrary+ <*> arbitrary+ <*> arbitrary+ <*> arbitrary+ <*> arbitrary+ shrink (FieldFormat a b c d e f g) = [ FieldFormat a' b' c' d' e' f' g' | (a', b', c', d', e', f', g') <- shrink (a, b, c, d, e, f, g) ]++#endif++-- ** Helper functions for implementing arbitrary++-- | Apply a binary function to random arguments.+applyArbitrary2 :: (Arbitrary a, Arbitrary b) => (a -> b -> r) -> Gen r+applyArbitrary2 f = liftA2 f arbitrary arbitrary++-- | Apply a ternary function to random arguments.+applyArbitrary3+ :: (Arbitrary a, Arbitrary b, Arbitrary c)+ => (a -> b -> c -> r) -> Gen r+applyArbitrary3 f = liftA3 f arbitrary arbitrary arbitrary++-- | Apply a function of arity 4 to random arguments.+applyArbitrary4+ :: (Arbitrary a, Arbitrary b, Arbitrary c, Arbitrary d)+ => (a -> b -> c -> d -> r) -> Gen r+applyArbitrary4 f = applyArbitrary3 (uncurry f)++-- | Generates an integral number. The number can be positive or negative+-- and its maximum absolute value depends on the size parameter.+arbitrarySizedIntegral :: Integral a => Gen a+arbitrarySizedIntegral+ | isNonNegativeType fromI = arbitrarySizedNatural+ | otherwise = sized $ \n -> inBounds fromI (chooseInt (-n, n))+ where+ -- NOTE: this is a trick to make sure we get around lack of scoped type+ -- variables by pinning the result-type of fromIntegral.+ fromI = fromIntegral++isNonNegativeType :: Enum a => (Int -> a) -> Bool+isNonNegativeType fromI =+ case enumFromThen (fromI 1) (fromI 0) of+ [_, _] -> True+ _ -> False++-- | Generates a natural number. The number's maximum value depends on+-- the size parameter.+arbitrarySizedNatural :: Integral a => Gen a+arbitrarySizedNatural =+ sized $ \n ->+ inBounds fromIntegral (chooseInt (0, n))++inBounds :: Integral a => (Int -> a) -> Gen Int -> Gen a+inBounds fi g = fmap fi (g `suchThat` (\x -> toInteger x == toInteger (fi x)))++-- | Uniformly generates a fractional number. The number can be positive or negative+-- and its maximum absolute value depends on the size parameter.+arbitrarySizedFractional :: Fractional a => Gen a+arbitrarySizedFractional =+ sized $ \n -> do+ denom <- chooseInt (1, max 1 n)+ numer <- chooseInt (-n*denom, n*denom)+ pure $ fromIntegral numer / fromIntegral denom++-- Useful for getting at minBound and maxBound without having to+-- fiddle around with asTypeOf.+{-# INLINE withBounds #-}+withBounds :: Bounded a => (a -> a -> Gen a) -> Gen a+withBounds k = k minBound maxBound++-- | Generates an integral number. The number is chosen uniformly from+-- the entire range of the type. You may want to use+-- 'arbitrarySizedBoundedIntegral' instead.+arbitraryBoundedIntegral :: (Bounded a, Integral a) => Gen a+arbitraryBoundedIntegral = chooseBoundedIntegral (minBound, maxBound)++-- | Generates an element of a bounded type. The element is+-- chosen from the entire range of the type.+arbitraryBoundedRandom :: (Bounded a, Random a) => Gen a+arbitraryBoundedRandom = choose (minBound,maxBound)++-- | Generates an element of a bounded enumeration.+arbitraryBoundedEnum :: (Bounded a, Enum a) => Gen a+arbitraryBoundedEnum = chooseEnum (minBound, maxBound)++-- | Generates an integral number from a bounded domain. The number is+-- chosen from the entire range of the type, but small numbers are+-- generated more often than big numbers. Inspired by demands from+-- Phil Wadler.+arbitrarySizedBoundedIntegral :: (Bounded a, Integral a) => Gen a+-- INLINEABLE so that this combinator gets specialised at each type,+-- which means that the constant 'bits' in the let-block below will+-- only be computed once.+{-# INLINEABLE arbitrarySizedBoundedIntegral #-}+arbitrarySizedBoundedIntegral =+ withBounds $ \mn mx ->+ let ilog2 1 = 0+ ilog2 n | n > 0 = 1 + ilog2 (n `div` 2)++ -- How many bits are needed to represent this type?+ -- (This number is an upper bound, not exact.)+ bits = ilog2 (toInteger mx - toInteger mn + 1) in+ sized $ \k ->+ let+ -- Reach maximum size by k=80, or quicker for small integer types+ power = ((bits `max` 40) * k) `div` 80++ -- Bounds should be 2^power, but:+ -- * clamp the result to minBound/maxBound+ -- * clamp power to 'bits', in case k is a huge number+ lo = toInteger mn `max` (-1 `shiftL` (power `min` bits))+ hi = toInteger mx `min` (1 `shiftL` (power `min` bits)) in+ fmap fromInteger (chooseInteger (lo, hi))++-- ** Generators for various kinds of character++-- | Generates any Unicode character (but not a surrogate)+arbitraryUnicodeChar :: Gen Char+arbitraryUnicodeChar =+ arbitraryBoundedEnum `suchThat` isValidUnicode+ where+ isValidUnicode c = case generalCategory c of+ Surrogate -> False+ NotAssigned -> False+ _ -> True++-- | Generates a random ASCII character (0-127).+arbitraryASCIIChar :: Gen Char+arbitraryASCIIChar = chooseEnum ('\0', '\127')++-- | Generates a printable Unicode character.+arbitraryPrintableChar :: Gen Char+arbitraryPrintableChar = arbitrary `suchThat` isPrint++-- ** Helper functions for implementing shrink++-- | Returns no shrinking alternatives.+shrinkNothing :: a -> [a]+shrinkNothing _ = []++-- | Map a shrink function to another domain. This is handy if your data type+-- has special invariants, but is /almost/ isomorphic to some other type.+--+-- @+-- shrinkOrderedList :: (Ord a, Arbitrary a) => [a] -> [[a]]+-- shrinkOrderedList = shrinkMap sort id+--+-- shrinkSet :: (Ord a, Arbitrary a) => Set a -> [Set a]+-- shrinkSet = shrinkMap fromList toList+-- @+shrinkMap :: Arbitrary a => (a -> b) -> (b -> a) -> b -> [b]+shrinkMap f g = shrinkMapBy f g shrink++-- | Non-overloaded version of `shrinkMap`.+shrinkMapBy :: (a -> b) -> (b -> a) -> (a -> [a]) -> b -> [b]+shrinkMapBy f g shr = map f . shr . g++-- | Shrink an integral number.+shrinkIntegral :: Integral a => a -> [a]+shrinkIntegral x =+ [ -x | x < 0, -x > x ] +++ [ x - i | i <- takeWhile (/= 0) (iterate (`quot` 2) x)]++-- | Shrink an element of a bounded enumeration.+--+-- === __Example__+--+-- @+-- data MyEnum = E0 | E1 | E2 | E3 | E4 | E5 | E6 | E7 | E8 | E9+-- deriving (Bounded, Enum, Eq, Ord, Show)+-- @+--+-- >>> shrinkBoundedEnum E9+-- [E0,E5,E7,E8]+--+-- >>> shrinkBoundedEnum E5+-- [E0,E3,E4]+--+-- >>> shrinkBoundedEnum E0+-- []+--+shrinkBoundedEnum :: (Bounded a, Enum a, Eq a) => a -> [a]+shrinkBoundedEnum a+ | a == minBound =+ []+ | otherwise =+ toEnum <$> filter (>= minBoundInt) (shrinkIntegral $ fromEnum a)+ where+ minBoundInt :: Int+ minBoundInt = fromEnum (minBound `asTypeOf` a)++-- | Shrink a fraction, preferring numbers with smaller+-- numerators or denominators. See also 'shrinkDecimal'.+shrinkRealFrac :: RealFrac a => a -> [a]+shrinkRealFrac x+ | not (x == x) = 0 : takeWhile (< 1000) numbers -- NaN+ | x > 0 && not (2*x+1>x) = 0 : takeWhile (<x) numbers -- infinity+ | x < 0 = negate x:map negate (shrinkRealFrac (negate x))+ | otherwise = -- x is finite and >= 0+ -- To ensure termination+ filter (\y -> abs y < abs x) $+ -- Try shrinking to an integer first+ map fromInteger (shrink (truncate x) ++ [truncate x]) +++ -- Shrink the numerator+ [fromRational (num' % denom) | num' <- shrink num] +++ -- Shrink the denominator, and keep the fraction as close+ -- to the original as possible, rounding towards zero+ [fromRational (truncate (num * denom' % denom) % denom')+ | denom' <- shrink denom, denom' /= 0 ]+ where+ num = numerator (toRational x)+ denom = denominator (toRational x)+ numbers = iterate (*2) 1++-- | Shrink a real number, preferring numbers with shorter+-- decimal representations. See also 'shrinkRealFrac'.+shrinkDecimal :: RealFrac a => a -> [a]+shrinkDecimal x+ | not (x == x) = 0 : takeWhile (< 1000) numbers -- NaN+ | not (2*abs x+1>abs x) = 0 : takeWhile (<x) numbers -- infinity+ | x < 0 = negate x:map negate (shrinkDecimal (negate x))+ | otherwise = -- x is finite and >= 0+ -- e.g. shrink pi =+ -- shrink 3 ++ map (/ 10) (shrink 31) +++ -- map (/ 100) (shrink 314) + ...,+ -- where the inner calls to shrink use integer shrinking.+ [ y+ | precision <- take 6 (iterate (*10) 1),+ let m = round (toRational x * precision),+ precision == 1 || m `mod` 10 /= 0, -- don't allow shrinking to increase digits+ n <- m:shrink m,+ let y = fromRational (fromInteger n / precision),+ abs y < abs x ]+ where+ -- 1, 2, 3, ..., 10, 20, 30, ..., 100, 200, 300, etc.+ numbers = concat $ iterate (map (*10)) (map fromInteger [1..9])++--------------------------------------------------------------------------+-- ** CoArbitrary++#ifndef NO_GENERICS+-- | Used for random generation of functions.+-- You should consider using 'Test.QuickCheck.Fun' instead, which+-- can show the generated functions as strings.+--+-- 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.+ -- You should use 'variant' to perturb the random generator;+ -- the goal is that different values for the first argument will+ -- lead to different calls to 'variant'. An example will help:+ --+ -- @+ -- instance CoArbitrary a => CoArbitrary [a] where+ -- 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'.+(><) :: (Gen a -> Gen a) -> (Gen a -> Gen a) -> (Gen a -> Gen a)+(><) = (.)++instance (Arbitrary a, CoArbitrary b) => CoArbitrary (a -> b) where+ coarbitrary f gen =+ do xs <- arbitrary+ coarbitrary (map f xs) gen++instance CoArbitrary () where+ coarbitrary _ = id++instance CoArbitrary Bool where+ coarbitrary False = variant 0+ coarbitrary True = variant 1++instance CoArbitrary Ordering where+ coarbitrary GT = variant 0+ coarbitrary EQ = variant 1+ coarbitrary LT = variant 2++instance CoArbitrary a => CoArbitrary (Maybe a) where+ coarbitrary Nothing = variant 0+ coarbitrary (Just x) = variant 1 . coarbitrary x++instance (CoArbitrary a, CoArbitrary b) => CoArbitrary (Either a b) where+ coarbitrary (Left x) = variant 0 . coarbitrary x+ coarbitrary (Right y) = variant 1 . coarbitrary y++instance CoArbitrary a => CoArbitrary [a] where+ coarbitrary [] = variant 0+ coarbitrary (x:xs) = variant 1 . coarbitrary (x,xs)++instance (Integral a, CoArbitrary a) => CoArbitrary (Ratio a) where+ coarbitrary r = coarbitrary (numerator r,denominator r)++#ifndef NO_FIXED+instance HasResolution a => CoArbitrary (Fixed a) where+ coarbitrary = coarbitraryReal+#endif++#if defined(MIN_VERSION_base)+instance CoArbitrary a => CoArbitrary (Complex a) where+#else+instance (RealFloat a, CoArbitrary a) => CoArbitrary (Complex a) where+#endif+ coarbitrary (x :+ y) = coarbitrary x . coarbitrary y++instance (CoArbitrary a, CoArbitrary b)+ => CoArbitrary (a,b)+ where+ coarbitrary (x,y) = coarbitrary x+ . coarbitrary y++instance (CoArbitrary a, CoArbitrary b, CoArbitrary c)+ => CoArbitrary (a,b,c)+ where+ coarbitrary (x,y,z) = coarbitrary x+ . coarbitrary y+ . coarbitrary z++instance (CoArbitrary a, CoArbitrary b, CoArbitrary c, CoArbitrary d)+ => CoArbitrary (a,b,c,d)+ where+ coarbitrary (x,y,z,v) = coarbitrary x+ . coarbitrary y+ . coarbitrary z+ . coarbitrary v++instance (CoArbitrary a, CoArbitrary b, CoArbitrary c, CoArbitrary d, CoArbitrary e)+ => CoArbitrary (a,b,c,d,e)+ where+ coarbitrary (x,y,z,v,w) = coarbitrary x+ . coarbitrary y+ . coarbitrary z+ . coarbitrary v+ . coarbitrary w++-- typical instance for primitive (numerical) types++instance CoArbitrary Integer where+ coarbitrary = coarbitraryIntegral++instance CoArbitrary Int where+ coarbitrary = coarbitraryIntegral++instance CoArbitrary Int8 where+ coarbitrary = coarbitraryIntegral++instance CoArbitrary Int16 where+ coarbitrary = coarbitraryIntegral++instance CoArbitrary Int32 where+ coarbitrary = coarbitraryIntegral++instance CoArbitrary Int64 where+ coarbitrary = coarbitraryIntegral++instance CoArbitrary Word where+ coarbitrary = coarbitraryIntegral++instance CoArbitrary Word8 where+ coarbitrary = coarbitraryIntegral++instance CoArbitrary Word16 where+ coarbitrary = coarbitraryIntegral++instance CoArbitrary Word32 where+ coarbitrary = coarbitraryIntegral++instance CoArbitrary Word64 where+ coarbitrary = coarbitraryIntegral++instance CoArbitrary Char where+ coarbitrary = coarbitrary . ord++instance CoArbitrary Float where+ coarbitrary = coarbitraryReal++instance CoArbitrary Double where+ coarbitrary = coarbitraryReal++#if defined(MIN_VERSION_base)+instance CoArbitrary Natural where+ coarbitrary = coarbitraryIntegral+#endif++-- Coarbitrary instances for container types+instance CoArbitrary a => CoArbitrary (Set.Set a) where+ coarbitrary = coarbitrary. Set.toList+instance (CoArbitrary k, CoArbitrary v) => CoArbitrary (Map.Map k v) where+ coarbitrary = coarbitrary . Map.toList+instance CoArbitrary IntSet.IntSet where+ coarbitrary = coarbitrary . IntSet.toList+instance CoArbitrary a => CoArbitrary (IntMap.IntMap a) where+ coarbitrary = coarbitrary . IntMap.toList+instance CoArbitrary a => CoArbitrary (Sequence.Seq a) where+ coarbitrary = coarbitrary . toList+instance CoArbitrary a => CoArbitrary (Tree.Tree a) where+ coarbitrary (Tree.Node val forest) = coarbitrary val . coarbitrary forest++-- CoArbitrary instance for Ziplist+instance CoArbitrary a => CoArbitrary (ZipList a) where+ coarbitrary = coarbitrary . getZipList++-- CoArbitrary instance for NonEmpty+#if defined(MIN_VERSION_base)+instance CoArbitrary a => CoArbitrary (NonEmpty a) where+ coarbitrary (a NonEmpty.:| as) = coarbitrary (a, as)+#endif++#ifndef NO_TRANSFORMERS+-- CoArbitrary instance for transformers' Functors+instance CoArbitrary a => CoArbitrary (Identity a) where+ coarbitrary = coarbitrary . runIdentity++instance CoArbitrary a => CoArbitrary (Constant a b) where+ coarbitrary = coarbitrary . getConstant+#endif++-- CoArbitrary instance for Const+instance CoArbitrary a => CoArbitrary (Const a b) where+ coarbitrary = coarbitrary . getConst++-- CoArbitrary instances for Monoid+instance CoArbitrary a => CoArbitrary (Monoid.Dual a) where+ coarbitrary = coarbitrary . Monoid.getDual++instance (Arbitrary a, CoArbitrary a) => CoArbitrary (Monoid.Endo a) where+ coarbitrary = coarbitrary . Monoid.appEndo++instance CoArbitrary Monoid.All where+ coarbitrary = coarbitrary . Monoid.getAll++instance CoArbitrary Monoid.Any where+ coarbitrary = coarbitrary . Monoid.getAny++instance CoArbitrary a => CoArbitrary (Monoid.Sum a) where+ coarbitrary = coarbitrary . Monoid.getSum++instance CoArbitrary a => CoArbitrary (Monoid.Product a) where+ coarbitrary = coarbitrary . Monoid.getProduct++#if defined(MIN_VERSION_base)+instance CoArbitrary a => CoArbitrary (Monoid.First a) where+ coarbitrary = coarbitrary . Monoid.getFirst++instance CoArbitrary a => CoArbitrary (Monoid.Last a) where+ coarbitrary = coarbitrary . Monoid.getLast++instance CoArbitrary (f a) => CoArbitrary (Monoid.Alt f a) where+ coarbitrary = coarbitrary . Monoid.getAlt++instance CoArbitrary a => CoArbitrary (Semigroup.Max a) where+ coarbitrary = coarbitrary . Semigroup.getMax++instance CoArbitrary a => CoArbitrary (Semigroup.Min a) where+ coarbitrary = coarbitrary . Semigroup.getMin++instance CoArbitrary a => CoArbitrary (Semigroup.First a) where+ coarbitrary = coarbitrary . Semigroup.getFirst++instance CoArbitrary a => CoArbitrary (Semigroup.Last a) where+ coarbitrary = coarbitrary . Semigroup.getLast++instance CoArbitrary Newline where+ coarbitrary LF = variant 0+ coarbitrary CRLF = variant 1++instance CoArbitrary NewlineMode where+ coarbitrary (NewlineMode inNL outNL) = coarbitrary inNL . coarbitrary outNL++instance (CoArbitrary a, CoArbitrary b) => CoArbitrary (Semigroup.Arg a b) where+ coarbitrary (Semigroup.Arg a b) = coarbitrary (a, b)++instance CoArbitrary GeneralCategory where+ coarbitrary = coarbitrary . fromEnum++instance CoArbitrary SeekMode where+ coarbitrary = coarbitrary . fromEnum++instance CoArbitrary IOMode where+#if !defined(__MHS__)+ coarbitrary = coarbitrary . fromEnum+#else+ coarbitrary ReadMode = variant 0+ coarbitrary WriteMode = variant 1+ coarbitrary AppendMode = variant 2+ coarbitrary ReadWriteMode = variant 3+#endif++instance CoArbitrary FieldFormat where+ coarbitrary ff = coarbitrary (fmtWidth ff)+ . coarbitrary (fmtPrecision ff)+ . coarbitrary (fmtAdjust ff)+ . coarbitrary (fmtSign ff)+ . coarbitrary (fmtAlternate ff)+ . coarbitrary (fmtModifiers ff)+ . coarbitrary (fmtChar ff)++instance CoArbitrary FormatParse where+ coarbitrary fp = coarbitrary (fpModifiers fp)+ . coarbitrary (fpChar fp)+ . coarbitrary (fpRest fp)++instance CoArbitrary FormatAdjustment where+ coarbitrary LeftAdjust = coarbitrary True+ coarbitrary ZeroPad = coarbitrary False++instance CoArbitrary FormatSign where+ coarbitrary SignPlus = coarbitrary True+ coarbitrary SignSpace = coarbitrary False++instance CoArbitrary BufferMode where+ coarbitrary = coarbitrary . embed+ where embed NoBuffering = Left True+ embed LineBuffering = Left False+ embed (BlockBuffering m) = Right m++instance CoArbitrary ExitCode where+ coarbitrary = coarbitrary . embed+ where embed ExitSuccess = Nothing+ embed (ExitFailure i) = Just i++#if !defined(__MHS__)+instance CoArbitrary TextEncoding where+ coarbitrary = coarbitrary . show -- No other way as far as I can tell :(+#endif++instance CoArbitrary a => CoArbitrary (Semigroup.WrappedMonoid a) where+ coarbitrary = coarbitrary . Semigroup.unwrapMonoid++#endif++instance CoArbitrary Version where+ coarbitrary (Version a b) = coarbitrary (a, b)++-- ** Helpers for implementing coarbitrary++-- | A 'coarbitrary' implementation for integral numbers.+coarbitraryIntegral :: Integral a => a -> Gen b -> Gen b+coarbitraryIntegral = variant++-- | A 'coarbitrary' implementation for real numbers.+coarbitraryReal :: Real a => a -> Gen b -> Gen b+coarbitraryReal x = coarbitrary (toRational x)++-- | 'coarbitrary' helper for lazy people :-).+coarbitraryShow :: Show a => a -> Gen b -> Gen b+coarbitraryShow x = coarbitrary (show x)++-- | A 'coarbitrary' implementation for enums.+coarbitraryEnum :: Enum a => a -> Gen b -> Gen b+coarbitraryEnum = variant . fromEnum++--------------------------------------------------------------------------+-- ** arbitrary generators++-- these are here and not in Gen because of the Arbitrary class constraint++-- | Generates a list of a given length.+vector :: Arbitrary a => Int -> Gen [a]+vector k = vectorOf k arbitrary++-- | Generates an ordered list.+orderedList :: (Ord a, Arbitrary a) => Gen [a]+orderedList = sort `fmap` arbitrary++-- | Generates an infinite list.+infiniteList :: Arbitrary a => Gen [a]+infiniteList = infiniteListOf arbitrary+++--------------------------------------------------------------------------+-- ** Rational helper++infixr 5 :<+data Stream a = !a :< Stream a++streamNth :: Int -> Stream a -> a+streamNth n (x :< xs) | n <= 0 = x+ | otherwise = streamNth (n - 1) xs++-- We read into this stream only with ~size argument, capped to 256,+-- so it's ok to have it as CAF. (256 chosen somewhat arbitrarily, the+-- point is just to stop this blowing up.)+--+rationalUniverse :: Stream Rational+rationalUniverse = 0 :< 1 :< (-1) :< go leftSideStream+ where+ go (x :< xs) =+ let nx = -x+ rx = recip x+ nrx = -rx+ in nx `seq` rx `seq` nrx `seq` (x :< rx :< nx :< nrx :< go xs)++-- All the rational numbers on the left side of the Calkin-Wilf tree,+-- in breadth-first order.+leftSideStream :: Stream Rational+leftSideStream = (1 % 2) :< go leftSideStream+ where+ go (x :< xs) =+ lChild `seq` rChild `seq`+ (lChild :< rChild :< go xs)+ where+ nd = numerator x + denominator x+ lChild = numerator x % nd+ rChild = nd % denominator x++--------------------------------------------------------------------------+-- the end.
+ src/Test/QuickCheck/Compat.hs view
@@ -0,0 +1,34 @@+-- This module provides tools to simplify compat code across different compiler and library versions+{-# LANGUAGE CPP #-}+module Test.QuickCheck.Compat where++#if MIN_VERSION_base(4,16,0)+import Data.Tuple+#endif++#if MIN_VERSION_base(4,16,0)++#if !MIN_VERSION_base(4,18,0)++getSolo :: Solo a -> a+getSolo (Solo a) = a++mkSolo :: a -> Solo a+mkSolo = Solo++#elif !MIN_VERSION_base(4,19,0)++getSolo :: Solo a -> a+getSolo (MkSolo a) = a++mkSolo :: a -> Solo a+mkSolo = MkSolo++#else++mkSolo :: a -> Solo a+mkSolo = MkSolo++#endif++#endif
+ src/Test/QuickCheck/Exception.hs view
@@ -0,0 +1,118 @@+-- | Throwing and catching exceptions. Internal QuickCheck module.++-- Hide away the nasty implementation-specific ways of catching+-- exceptions behind a nice API. The main trouble is catching ctrl-C.++{-# OPTIONS_HADDOCK hide #-}+{-# LANGUAGE CPP #-}+#ifndef NO_SAFE_HASKELL+{-# LANGUAGE Safe #-}+#endif+module Test.QuickCheck.Exception where++#if (!defined(__GLASGOW_HASKELL__) || (__GLASGOW_HASKELL__ < 700)) && !defined(__MHS__)+#define OLD_EXCEPTIONS+#endif++#if defined(NO_EXCEPTIONS)+#else+import qualified Control.Exception as E+#endif++#if defined(NO_EXCEPTIONS)+type AnException = ()+#elif defined(OLD_EXCEPTIONS)+type AnException = E.Exception+#else+type AnException = E.SomeException+#endif++#ifdef NO_EXCEPTIONS+tryEvaluate :: a -> IO (Either AnException a)+tryEvaluate x = return (Right x)++tryEvaluateIO :: IO a -> IO (Either AnException a)+tryEvaluateIO m = fmap Right m++evaluate :: a -> IO a+evaluate x = x `seq` return x++isInterrupt :: AnException -> Bool+isInterrupt _ = False++discard :: a+discard = error "'discard' not supported, since your Haskell system can't catch exceptions"++isDiscard :: AnException -> Bool+isDiscard _ = False++finally :: IO a -> IO b -> IO a+finally mx my = do+ x <- mx+ my+ return x++#else+--------------------------------------------------------------------------+-- try evaluate++tryEvaluate :: a -> IO (Either AnException a)+tryEvaluate x = tryEvaluateIO (return x)++tryEvaluateIO :: IO a -> IO (Either AnException a)+tryEvaluateIO m = E.tryJust notAsync (m >>= E.evaluate)+ where+ notAsync :: AnException -> Maybe AnException+#if defined(MIN_VERSION_base)+ notAsync e = case E.fromException e of+ Just (E.SomeAsyncException _) -> Nothing+ Nothing -> Just e+#elif !defined(OLD_EXCEPTIONS)+ notAsync e = case E.fromException e :: Maybe E.AsyncException of+ Just _ -> Nothing+ Nothing -> Just e+#else+ notAsync e = Just e+#endif++evaluate :: a -> IO a+evaluate = E.evaluate++-- | Test if an exception was a @^C@.+-- QuickCheck won't try to shrink an interrupted test case.+isInterrupt :: AnException -> Bool++#if defined(OLD_EXCEPTIONS)+isInterrupt _ = False+#else+isInterrupt e = E.fromException e == Just E.UserInterrupt+#endif++-- | A special error value. If a property evaluates 'discard', it+-- causes QuickCheck to discard the current test case.+-- This can be useful if you want to discard the current test case,+-- but are somewhere you can't use 'Test.QuickCheck.==>', such as inside a+-- generator.+discard :: a++isDiscard :: AnException -> Bool+(discard, isDiscard) = (E.throw (E.ErrorCall msg), isDiscard)+ where+ msg = "DISCARD. " +++ "You should not see this exception, it is internal to QuickCheck."+#if defined(OLD_EXCEPTIONS)+ isDiscard (E.ErrorCall msg') = msg' == msg+ isDiscard _ = False+#else+ isDiscard e =+ case E.fromException e of+ Just (E.ErrorCall msg') -> msg' == msg+ _ -> False+#endif++finally :: IO a -> IO b -> IO a+finally = E.finally+#endif++--------------------------------------------------------------------------+-- the end.
+ src/Test/QuickCheck/Features.hs view
@@ -0,0 +1,108 @@+{-# LANGUAGE CPP #-}+#ifndef NO_SAFE_HASKELL+{-# LANGUAGE Safe #-}+#endif+{-# 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 Test.QuickCheck.Random+import qualified Data.Set as Set+import Data.Set(Set)+import Data.List (intersperse)+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 (map fst $ filter snd $ 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+-- []+-- []+-- <BLANKLINE>+-- *** Found example of count x xs == 1+-- 0+-- [0]+-- []+-- <BLANKLINE>+-- *** Found example of count x xs >= 2+-- 5+-- [5,5]+-- [5]+-- <BLANKLINE>+-- +++ 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 = loop Set.empty $ replay args+ where+ loop :: Set String -> Maybe (QCGen, Int) -> IO Result+ loop feats replay = withNullTerminal $ \nullterm -> do+ res <- quickCheckWithResult args{chatty = False, replay = replay} (prop_noNewFeatures feats prop)+ let feats' = features (failingLabels res) (failingClasses res)+ case res of+ Failure{reason = "New feature found"} -> do+ putStrLn $+ "*** Found example of " +++ concat (intersperse ", " (Set.toList (feats' Set.\\ feats)))+ mapM_ putStrLn (failingTestCase res)+ putStrLn ""+ loop (Set.union feats feats') (Just (usedSeed res, usedSize res))+ _ -> do+ out <- terminalOutput nullterm+ putStr out+ return res+ at0 f s 0 0 = s+ at0 f s n d = f n d
+ src/Test/QuickCheck/Function.hs view
@@ -0,0 +1,772 @@+{-# LANGUAGE TypeOperators, GADTs, CPP, Rank2Types #-}+#ifndef NO_SAFE_HASKELL+{-# LANGUAGE Trustworthy #-}+#endif+#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ >= 708+{-# LANGUAGE PatternSynonyms, ViewPatterns #-}+#endif++#ifndef NO_GENERICS+{-# LANGUAGE DefaultSignatures, FlexibleContexts #-}+#endif++#ifndef NO_POLYKINDS+{-# LANGUAGE PolyKinds #-}+#endif++-- | Generation of random shrinkable, showable functions.+-- See the paper \"Shrinking and showing functions\" by Koen Claessen.+--+-- __Note__: most of the contents of this module are re-exported by+-- "Test.QuickCheck". You probably do not need to import it directly.+--+-- Example of use:+--+-- >>> :{+-- >>> let prop :: Fun String Integer -> Bool+-- >>> prop (Fun _ f) = f "monkey" == f "banana" || f "banana" == f "elephant"+-- >>> :}+-- >>> quickCheck prop+-- *** Failed! Falsified (after 3 tests and 134 shrinks):+-- {"elephant"->1, "monkey"->1, _->0}+--+-- To generate random values of type @'Fun' a b@,+-- you must have an instance @'Function' a@.+-- If your type has a 'Show' instance, you can use 'functionShow' to write the instance; otherwise,+-- use 'functionMap' to give a bijection between your type and a type that is already an instance of 'Function'.+-- See the @'Function' [a]@ instance for an example of the latter.+module Test.QuickCheck.Function+ ( Fun(..)+ , mkFun+ , applyFun+ , apply+ , applyFun2+ , applyFun3+ , (:->)+ , Function(..)+ , functionMap+ , functionShow+ , functionIntegral+ , functionRealFrac+ , functionBoundedEnum+ , functionElements+ , functionVoid+ , functionMapWith+ , functionEitherWith+ , functionPairWith+#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ >= 708+ , pattern Fn+ , pattern Fn2+ , pattern Fn3+#endif+ )+ where++--------------------------------------------------------------------------+-- imports++import Test.QuickCheck.Arbitrary+import Test.QuickCheck.Poly++import Control.Applicative+import Data.Char+import Data.Word+import Data.List( intersperse )+import Data.Ratio+import qualified Data.IntMap as IntMap+import qualified Data.IntSet as IntSet+import qualified Data.Map as Map+import qualified Data.Set as Set+import qualified Data.Sequence as Sequence+import qualified Data.Tree as Tree+import Data.Int+import Data.Complex+import Data.Foldable(toList)+import Data.Functor.Identity+import qualified Data.Monoid as Monoid+import qualified Data.Semigroup as Semigroup+import qualified Data.List.NonEmpty as NonEmpty+import Numeric.Natural+import qualified Data.Bits as Bits+import Data.Tuple+import Data.Ord+import Data.Functor.Contravariant+import Text.Printf+import System.IO+import System.Exit+import Data.Version+import Data.Array.Byte+import qualified GHC.Exts as Exts++#if defined(__MHS__)+import Data.ZipList+import Control.WrappedMonad+#endif++#if defined(MIN_VERSION_base)+import System.IO+ ( Newline(..)+ , NewlineMode(..)+ )+#endif++#ifndef NO_FIXED+import Data.Fixed+#endif++#ifndef NO_GENERICS+import GHC.Generics hiding (C)+#endif++import Test.QuickCheck.Compat++--------------------------------------------------------------------------+-- 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)+ Unit :: c -> (() :-> c)+ Nil :: a :-> c+ Table :: Eq a => [(a,c)] -> (a :-> c)+ Map :: (a -> b) -> (b -> a) -> (b :-> c) -> (a :-> c)++instance Functor ((:->) a) where+ fmap f (Pair p) = Pair (fmap (fmap f) p)+ fmap f (p:+:q) = fmap f p :+: fmap f q+ fmap f (Unit c) = Unit (f c)+ fmap f Nil = Nil+ fmap f (Table xys) = Table [ (x,f y) | (x,y) <- xys ]+ fmap f (Map g h p) = Map g h (fmap f p)++instance (Show a, Show b) => Show (a:->b) where+ show p = showFunction p Nothing++-- only use this on finite functions+showFunction :: (Show a, Show b) => (a :-> b) -> Maybe b -> String+showFunction p md =+ "{" ++ concat (intersperse ", " ( [ show x ++ "->" ++ show c+ | (x,c) <- table p+ ]+ ++ [ "_->" ++ show d+ | Just d <- [md]+ ] )) ++ "}"++-- turning a concrete function into an abstract function (with a default result)+abstract :: (a :-> c) -> c -> (a -> c)+#if defined(__MHS__)+{- This is a temporary fix for a deficiency in the MicroHs type checker. -}+abstract (Pair p) d xy =+ case xy of+ (x,y) -> abstract (fmap (\q -> abstract q d y) p) d x+#else+abstract (Pair p) d (x,y) = abstract (fmap (\q -> abstract q d y) p) d x+#endif+abstract (p :+: q) d exy = either (abstract p d) (abstract q d) exy+abstract (Unit c) _ _ = c+abstract Nil d _ = d+abstract (Table xys) d x = head ([y | (x',y) <- xys, x == x'] ++ [d])+abstract (Map g _ p) d x = abstract p d (g x)++-- generating a table from a concrete function+table :: (a :-> c) -> [(a,c)]+table (Pair p) = [ ((x,y),c) | (x,q) <- table p, (y,c) <- table q ]+table (p :+: q) = [ (Left x, c) | (x,c) <- table p ]+ ++ [ (Right y,c) | (y,c) <- table q ]+table (Unit c) = [ ((), c) ]+table Nil = []+table (Table xys) = xys+table (Map _ h p) = [ (h x, c) | (x,c) <- table p ]++--------------------------------------------------------------------------+-- Function++-- | The class @Function a@ is used for random generation of showable+-- functions of type @a -> b@.+--+-- There is a default implementation for 'function', which you can use+-- if your type has structural equality. Otherwise, you can normally+-- use 'functionMap' or 'functionShow'.+class Function a where+ function :: (a->b) -> (a:->b)+#ifndef NO_GENERICS+ default function :: (Generic a, GFunction (Rep a)) => (a->b) -> (a:->b)+ function = genericFunction+#endif++-- basic instances++-- | Provides a 'Function' instance for types with 'Bounded' and 'Enum'.+-- Use only for small types (i.e. not integers): creates+-- the list @['minBound'..'maxBound']@!+functionBoundedEnum :: (Eq a, Bounded a, Enum a) => (a->b) -> (a:->b)+functionBoundedEnum = functionElements [minBound..maxBound]++-- | Provides a 'Function' instance for small finite types.+functionElements :: Eq a => [a] -> (a->b) -> (a:->b)+functionElements xs f = Table [(x,f x) | x <- xs]++-- | Provides a 'Function' instance for types with 'RealFrac'.+functionRealFrac :: RealFrac a => (a->b) -> (a:->b)+functionRealFrac = functionMap toRational fromRational++-- | Provides a 'Function' instance for types with 'Integral'.+functionIntegral :: Integral a => (a->b) -> (a:->b)+functionIntegral = functionMap fromIntegral fromInteger++-- | Provides a 'Function' instance for types with 'Show' and 'Read'.+functionShow :: (Show a, Read a) => (a->c) -> (a:->c)+functionShow f = functionMap show read f++-- | Provides a 'Function' instance for types isomorphic to 'Data.Void.Void'.+--+-- An actual @'Function' 'Data.Void.Void'@ instance is defined in+-- @quickcheck-instances@.+functionVoid :: (forall b. void -> b) -> void :-> c+functionVoid _ = Nil++-- | The basic building block for 'Function' instances.+-- Provides a 'Function' instance by mapping to and from a type that+-- already has a 'Function' instance.+functionMap :: Function b => (a->b) -> (b->a) -> (a->c) -> (a:->c)+functionMap = functionMapWith function++-- | @since 2.13.3+functionMapWith :: ((b->c) -> (b:->c)) -> (a->b) -> (b->a) -> (a->c) -> (a:->c)+functionMapWith function g h f = Map g h (function (\b -> f (h b)))++instance Function () where+ function f = Unit (f ())++instance Function a => Function (Const a b) where+ function = functionMap getConst Const++instance Function a => Function (Identity a) where+ function = functionMap runIdentity Identity++instance (Function a, Function b) => Function (a,b) where+ function = functionPairWith function function++-- | @since 2.13.3+functionPairWith :: ((a->b->c) -> (a:->(b->c))) -> ((b->c) -> (b:->c)) -> ((a,b)->c) -> ((a,b):->c)+functionPairWith func1 func2 f = Pair (func2 `fmap` func1 (curry f))++instance (Function a, Function b) => Function (Either a b) where+ function = functionEitherWith function function++-- | @since 2.13.3+functionEitherWith :: ((a->c) -> (a:->c)) -> ((b->c) -> (b:->c)) -> (Either a b->c) -> (Either a b:->c)+functionEitherWith func1 func2 f = func1 (f . Left) :+: func2 (f . Right)++-- tuple convenience instances++instance (Function a, Function b, Function c) => Function (a,b,c) where+ function = functionMap (\(a,b,c) -> (a,(b,c))) (\(a,(b,c)) -> (a,b,c))++instance (Function a, Function b, Function c, Function d) => Function (a,b,c,d) where+ function = functionMap (\(a,b,c,d) -> (a,(b,c,d))) (\(a,(b,c,d)) -> (a,b,c,d))++instance (Function a, Function b, Function c, Function d, Function e) => Function (a,b,c,d,e) where+ function = functionMap (\(a,b,c,d,e) -> (a,(b,c,d,e))) (\(a,(b,c,d,e)) -> (a,b,c,d,e))++instance (Function a, Function b, Function c, Function d, Function e, Function f) => Function (a,b,c,d,e,f) where+ function = functionMap (\(a,b,c,d,e,f) -> (a,(b,c,d,e,f))) (\(a,(b,c,d,e,f)) -> (a,b,c,d,e,f))++instance (Function a, Function b, Function c, Function d, Function e, Function f, Function g) => Function (a,b,c,d,e,f,g) where+ function = functionMap (\(a,b,c,d,e,f,g) -> (a,(b,c,d,e,f,g))) (\(a,(b,c,d,e,f,g)) -> (a,b,c,d,e,f,g))++-- other instances++instance Function a => Function [a] where+ function = functionMap g h+ where+ g [] = Left ()+ g (x:xs) = Right (x,xs)++ h (Left _) = []+ h (Right (x,xs)) = x:xs++instance Function a => Function (NonEmpty.NonEmpty a) where+ function = functionMap (\(a NonEmpty.:| as) -> (a, as)) (\(a, as) -> a NonEmpty.:| as)++instance Function a => Function (ZipList a) where+ function = functionMap getZipList ZipList++instance Function a => Function (Maybe a) where+ function = functionMap g h+ where+ g Nothing = Left ()+ g (Just x) = Right x++ h (Left _) = Nothing+ h (Right x) = Just x++instance Function Bool where+ function = functionMap g h+ where+ g False = Left ()+ g True = Right ()++ h (Left _) = False+ h (Right _) = True++instance Function Integer where+ function = functionMap gInteger hInteger+ where+ gInteger n | n < 0 = Left (gNatural (abs n - 1))+ | otherwise = Right (gNatural n)++ hInteger (Left ws) = -(hNatural ws + 1)+ hInteger (Right ws) = hNatural ws++ gNatural 0 = []+ gNatural n = (fromIntegral (n `mod` 256) :: Word8) : gNatural (n `div` 256)++ hNatural [] = 0+ hNatural (w:ws) = fromIntegral w + 256 * hNatural ws++instance Function Int where+ function = functionIntegral++instance Function Word where+ function = functionIntegral++instance Function Char where+ function = functionMap ord chr++instance Function Float where+ function = functionRealFrac++instance Function Double where+ function = functionRealFrac++instance Function Natural where+ function = functionIntegral++-- instances for assorted types in the base package++instance Function Ordering where+ function = functionMap g h+ where+ g LT = Left False+ g EQ = Left True+ g GT = Right ()++ h (Left False) = LT+ h (Left True) = EQ+ h (Right _) = GT++instance (Integral a, Function a) => Function (Ratio a) where+ function = functionMap g h+ where+ g r = (numerator r, denominator r)+ h (n, d) = n % d++#ifndef NO_FIXED+instance HasResolution a => Function (Fixed a) where+ function = functionRealFrac+#endif++instance (RealFloat a, Function a) => Function (Complex a) where+ function = functionMap g h+ where+ g (x :+ y) = (x, y)+ h (x, y) = x :+ y++instance (Ord a, Function a) => Function (Set.Set a) where+ function = functionMap Set.toList Set.fromList++instance (Ord a, Function a, Function b) => Function (Map.Map a b) where+ function = functionMap Map.toList Map.fromList++instance Function IntSet.IntSet where+ function = functionMap IntSet.toList IntSet.fromList++instance Function a => Function (IntMap.IntMap a) where+ function = functionMap IntMap.toList IntMap.fromList++instance Function a => Function (Sequence.Seq a) where+ function = functionMap toList Sequence.fromList++instance Function a => Function (Tree.Tree a) where+ function = functionMap (\(Tree.Node x xs) -> (x,xs)) (uncurry Tree.Node)++instance Function Int8 where+ function = functionBoundedEnum++instance Function Int16 where+ function = functionIntegral++instance Function Int32 where+ function = functionIntegral++instance Function Int64 where+ function = functionIntegral++instance Function Word8 where+ function = functionBoundedEnum++instance Function Word16 where+ function = functionIntegral++instance Function Word32 where+ function = functionIntegral++instance Function Word64 where+ function = functionIntegral++#if defined(MIN_VERSION_base)+instance Function Newline where+ function = functionMap g h+ where+ g LF = False+ g CRLF = True++ h False = LF+ h True = CRLF++instance Function NewlineMode where+ function = functionMap g h+ where+ g (NewlineMode inNL outNL) = (inNL,outNL)+ h (inNL,outNL) = NewlineMode inNL outNL+#endif++-- instances for Data.Monoid newtypes++instance Function a => Function (Monoid.Dual a) where+ function = functionMap Monoid.getDual Monoid.Dual++instance Function Monoid.All where+ function = functionMap Monoid.getAll Monoid.All++instance Function Monoid.Any where+ function = functionMap Monoid.getAny Monoid.Any++instance Function a => Function (Monoid.Sum a) where+ function = functionMap Monoid.getSum Monoid.Sum++instance Function a => Function (Monoid.Product a) where+ function = functionMap Monoid.getProduct Monoid.Product++instance Function a => Function (Monoid.First a) where+ function = functionMap Monoid.getFirst Monoid.First++instance Function a => Function (Monoid.Last a) where+ function = functionMap Monoid.getLast Monoid.Last++instance Function (f a) => Function (Monoid.Alt f a) where+ function = functionMap Monoid.getAlt Monoid.Alt++instance Function a => Function (Semigroup.Min a) where+ function = functionMap Semigroup.getMin Semigroup.Min++instance Function a => Function (Semigroup.Max a) where+ function = functionMap Semigroup.getMax Semigroup.Max++instance Function a => Function (Semigroup.Last a) where+ function = functionMap Semigroup.getLast Semigroup.Last++instance Function a => Function (Semigroup.First a) where+ function = functionMap Semigroup.getFirst Semigroup.First++instance Function a => Function (Semigroup.WrappedMonoid a) where+ function = functionMap Semigroup.unwrapMonoid Semigroup.WrapMonoid++instance (Function a, Function b) => Function (Semigroup.Arg a b) where+ function = functionMap (\(Semigroup.Arg a b) -> (a, b)) (uncurry Semigroup.Arg)++#if MIN_VERSION_base(4,16,0)+instance Function a => Function (Bits.And a) where+ function = functionMap Bits.getAnd Bits.And++instance Function a => Function (Bits.Ior a) where+ function = functionMap Bits.getIor Bits.Ior++instance Function a => Function (Bits.Xor a) where+ function = functionMap Bits.getXor Bits.Xor++instance Function a => Function (Bits.Iff a) where+ function = functionMap Bits.getIff Bits.Iff+#endif++instance Function FormatSign where+ function = functionMap (\x -> case x of SignPlus -> True; _ -> False) (\b -> if b then SignPlus else SignSpace)++instance Function FormatAdjustment where+ function = functionMap (\x -> case x of LeftAdjust -> True; _ -> False) (\b -> if b then LeftAdjust else ZeroPad)++instance Function FormatParse where+ function = functionMap to from+ where to fp = (fpModifiers fp, fpChar fp, fpRest fp)+ from (a, b, c) = FormatParse a b c++instance Function FieldFormat where+ function = functionMap to from+ where to ff = ( fmtWidth ff+ , fmtPrecision ff+ , fmtAdjust ff+ , fmtSign ff+ , fmtAlternate ff+ , fmtModifiers ff+ , fmtChar ff)+ from (a, b, c, d, e, f, g) = FieldFormat a b c d e f g++instance Function GeneralCategory where+ function = functionBoundedEnum++instance Function SeekMode where+ function = functionElements [AbsoluteSeek, RelativeSeek, SeekFromEnd]++instance Function IOMode where+ function = functionElements [ReadMode, WriteMode, AppendMode, ReadWriteMode]++instance Function BufferMode where+ function = functionMap to from+ where to NoBuffering = Left True+ to LineBuffering = Left False+ to (BlockBuffering m) = Right m++ from (Left True) = NoBuffering+ from (Left False) = LineBuffering+ from (Right m) = BlockBuffering m++instance Function ExitCode where+ function = functionMap to from+ where to ExitSuccess = Nothing+ to (ExitFailure c) = Just c++ from Nothing = ExitSuccess+ from (Just c) = ExitFailure c++instance Function Version where+ function = functionMap to from+ where to (Version a b) = (a, b)+ from (a, b) = Version a b++#if !defined(__MHS__)+instance Function ByteArray where+ function = functionMap Exts.toList Exts.fromList+#endif++#if MIN_VERSION_base(4,16,0)+instance Function a => Function (Solo a) where+ function = functionMap getSolo mkSolo+#endif++instance Function a => Function (Down a) where+ function = functionMap getDown Down++#if !MIN_VERSION_base(4,15,0)+instance Function a => Function (Semigroup.Option a) where+ function = functionMap Semigroup.getOption Semigroup.Option+#endif++-- poly instances++instance Function A where+ function = functionMap unA A++instance Function B where+ function = functionMap unB B++instance Function C where+ function = functionMap unC C++instance Function OrdA where+ function = functionMap unOrdA OrdA++instance Function OrdB where+ function = functionMap unOrdB OrdB++instance Function OrdC where+ function = functionMap unOrdC OrdC++-- instance Arbitrary++instance (Function a, CoArbitrary a, Arbitrary b) => Arbitrary (a:->b) where+ arbitrary = function `fmap` arbitrary+ shrink = shrinkFun shrink++--------------------------------------------------------------------------+-- generic function instances++#ifndef NO_GENERICS+-- | Generic 'Function' implementation.+genericFunction :: (Generic a, GFunction (Rep a)) => (a->b) -> (a:->b)+genericFunction = functionMapWith gFunction from to++class GFunction f where+ gFunction :: (f a -> b) -> (f a :-> b)++instance GFunction U1 where+ gFunction = functionMap (\U1 -> ()) (\() -> U1)++instance (GFunction f, GFunction g) => GFunction (f :*: g) where+ gFunction = functionMapWith (functionPairWith gFunction gFunction) g h+ where+ g (x :*: y) = (x, y)+ h (x, y) = x :*: y++instance (GFunction f, GFunction g) => GFunction (f :+: g) where+ gFunction = functionMapWith (functionEitherWith gFunction gFunction) g h+ where+ g (L1 x) = Left x+ g (R1 x) = Right x+ h (Left x) = L1 x+ h (Right x) = R1 x++instance GFunction f => GFunction (M1 i c f) where+ gFunction = functionMapWith gFunction (\(M1 x) -> x) M1++instance Function a => GFunction (K1 i a) where+ gFunction = functionMap (\(K1 x) -> x) K1+#endif++--------------------------------------------------------------------------+-- shrinking++shrinkFun :: (c -> [c]) -> (a :-> c) -> [a :-> c]+shrinkFun shr (Pair p) =+ [ pair p' | p' <- shrinkFun (\q -> shrinkFun shr q) p ]+ where+ pair Nil = Nil+ pair p = Pair p++shrinkFun shr (p :+: q) =+ [ p .+. Nil | not (isNil q) ] +++ [ Nil .+. q | not (isNil p) ] +++ [ p .+. q' | q' <- shrinkFun shr q ] +++ [ p' .+. q | p' <- shrinkFun shr p ]+ where+ isNil :: (a :-> b) -> Bool+ isNil Nil = True+ isNil _ = False++ Nil .+. Nil = Nil+ p .+. q = p :+: q++shrinkFun shr (Unit c) =+ [ Nil ] +++ [ Unit c' | c' <- shr c ]++shrinkFun shr (Table xys) =+ [ table xys' | xys' <- shrinkList shrXy xys ]+ where+ shrXy (x,y) = [(x,y') | y' <- shr y]++ table :: Eq aa => [(aa,cc)] -> (aa :-> cc) -- MicroHs needs this+ table [] = Nil+ table xys = Table xys++shrinkFun shr Nil =+ []++shrinkFun shr (Map g h p) =+ [ mapp g h p' | p' <- shrinkFun shr p ]+ where+ mapp g h Nil = Nil+ mapp g h p = Map g h p++--------------------------------------------------------------------------+-- the Fun modifier++-- | Generation of random shrinkable, showable functions.+--+-- To generate random values of type @'Fun' a b@,+-- you must have an instance @'Function' a@.+--+-- See also 'applyFun', and 'Fn' with GHC >= 7.8.+data Fun a b = Fun (a :-> b, b, Shrunk) (a -> b)+data Shrunk = Shrunk | NotShrunk deriving Eq++instance Functor (Fun a) where+ fmap f (Fun (p, d, s) g) = Fun (fmap f p, f d, s) (f . g)++#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ >= 708+-- | A modifier for testing functions.+--+-- > prop :: Fun String Integer -> Bool+-- > prop (Fn f) = f "banana" == f "monkey"+-- > || f "banana" == f "elephant"+#if __GLASGOW_HASKELL__ >= 800+pattern Fn :: (a -> b) -> Fun a b+#endif+pattern Fn f <- (applyFun -> f)+#if __GLASGOW_HASKELL__ >= 802+{-# COMPLETE Fn #-}+#endif++-- | A modifier for testing binary functions.+--+-- > prop_zipWith :: Fun (Int, Bool) Char -> [Int] -> [Bool] -> Bool+-- > prop_zipWith (Fn2 f) xs ys = zipWith f xs ys == [ f x y | (x, y) <- zip xs ys]+#if __GLASGOW_HASKELL__ >= 800+pattern Fn2 :: (a -> b -> c) -> Fun (a, b) c+#endif+pattern Fn2 f <- (applyFun2 -> f)+#if __GLASGOW_HASKELL__ >= 802+{-# COMPLETE Fn2 #-}+#endif++-- | A modifier for testing ternary functions.+#if __GLASGOW_HASKELL__ >= 800+pattern Fn3 :: (a -> b -> c -> d) -> Fun (a, b, c) d+#endif+pattern Fn3 f <- (applyFun3 -> f)+#if __GLASGOW_HASKELL__ >= 802+{-# COMPLETE Fn3 #-}+#endif+#endif++-- | Create a `Fun` from a function representation and a default value (in case the function+-- is partial).+mkFun :: (a :-> b) -> b -> Fun a b+mkFun p d = Fun (p, d, NotShrunk) (abstract p d)++-- | Alias to 'applyFun'.+apply :: Fun a b -> (a -> b)+apply = applyFun++-- | Extracts the value of a function.+--+-- 'Fn' is the pattern equivalent of this function.+--+-- > prop :: Fun String Integer -> Bool+-- > prop f = applyFun f "banana" == applyFun f "monkey"+-- > || applyFun f "banana" == applyFun f "elephant"+applyFun :: Fun a b -> (a -> b)+applyFun (Fun _ f) = f++-- | Extracts the value of a binary function.+--+-- 'Fn2' is the pattern equivalent of this function.+--+-- > prop_zipWith :: Fun (Int, Bool) Char -> [Int] -> [Bool] -> Bool+-- > prop_zipWith f xs ys = zipWith (applyFun2 f) xs ys == [ applyFun2 f x y | (x, y) <- zip xs ys]+--+applyFun2 :: Fun (a, b) c -> (a -> b -> c)+applyFun2 (Fun _ f) a b = f (a, b)++-- | Extracts the value of a ternary function. 'Fn3' is the+-- pattern equivalent of this function.+applyFun3 :: Fun (a, b, c) d -> (a -> b -> c -> d)+applyFun3 (Fun _ f) a b c = f (a, b, c)++instance (Show a, Show b) => Show (Fun a b) where+ show (Fun (_, _, NotShrunk) _) = "<fun>"+ show (Fun (p, d, Shrunk) _) = showFunction p (Just d)++instance (Function a, CoArbitrary a, Arbitrary b) => Arbitrary (Fun a b) where+ arbitrary =+ do p <- arbitrary+ d <- arbitrary+ return (mkFun p d)++ shrink (Fun (p, d, s) f) =+ [ mkFun p' d' | (p', d') <- shrink (p, d) ] +++ [ Fun (p, d, Shrunk) f | s == NotShrunk ]++--------------------------------------------------------------------------+-- the end.
+ src/Test/QuickCheck/Gen.hs view
@@ -0,0 +1,385 @@+{-# LANGUAGE CPP #-}+#ifndef NO_ST_MONAD+{-# LANGUAGE Rank2Types #-}+#endif+#ifndef NO_SAFE_HASKELL+{-# LANGUAGE Safe #-}+#endif+-- | Test case generation.+--+-- __Note__: the contents of this module (except for the definition of+-- 'Gen') are re-exported by "Test.QuickCheck". You probably do not+-- need to import it directly.+module Test.QuickCheck.Gen where++--------------------------------------------------------------------------+-- imports++import Test.QuickCheck.Exception++import System.Random+ ( Random+ , random+ , randomR+ )++import Control.Monad+ ( ap+ , replicateM+ , filterM+ )++import Control.Monad.Fix+ ( MonadFix(..) )++import Control.Applicative+ ( Applicative(..) )++import Test.QuickCheck.Random+import Data.List (sortBy)+import Data.Ord+import Data.Maybe+#ifndef NO_SPLITMIX+import System.Random.SplitMix(bitmaskWithRejection64', nextInteger, nextDouble, nextFloat, SMGen)+#endif+import Data.Word+import Data.Int+import Data.Bits+import Control.Applicative+#ifndef NO_CALLSTACK+import GHC.Stack+#define WITHCALLSTACK(ty) HasCallStack => ty+#else+#define WITHCALLSTACK(ty) ty+#endif++--------------------------------------------------------------------------+-- ** Generator type++-- | A generator for values of type @a@.+--+-- The third-party packages+-- <http://hackage.haskell.org/package/QuickCheck-GenT QuickCheck-GenT>+-- and+-- <http://hackage.haskell.org/package/quickcheck-transformer quickcheck-transformer>+-- provide monad transformer versions of @Gen@.+newtype Gen a = MkGen{+ unGen :: QCGen -> Int -> a -- ^ Run the generator on a particular seed and size.+ -- If you just want to get a random value out, consider using 'generate'.+ }++instance Functor Gen where+ fmap f (MkGen h) =+ MkGen (\r n -> f (h r n))++instance Applicative Gen where+ pure x =+ MkGen (\_ _ -> x)+ (<*>) = ap++#ifndef NO_EXTRA_METHODS_IN_APPLICATIVE+ -- We don't need to split the seed for these.+ _ *> m = m+ m <* _ = m+#endif++instance Monad Gen where+ return = pure++ MkGen m >>= k =+ MkGen (\r n ->+ case splitImpl r of+ (r1, r2) ->+ let MkGen m' = k (m r1 n)+ in m' r2 n+ )++ (>>) = (*>)++instance MonadFix Gen where+ mfix f =+ MkGen $ \r n ->+ let a = unGen (f a) r n+ in a++--------------------------------------------------------------------------+-- ** Primitive generator combinators++-- | Modifies a generator using an integer seed.+variant :: Integral n => n -> Gen a -> Gen a+variant k (MkGen g) = MkGen (\r n -> g (integerVariant (toInteger k) $! r) n)++-- | Used to construct generators that depend on the size parameter.+--+-- For example, 'listOf', which uses the size parameter as an upper bound on+-- length of lists it generates, can be defined like this:+--+-- > listOf :: Gen a -> Gen [a]+-- > listOf gen = sized $ \n ->+-- > do k <- choose (0,n)+-- > vectorOf k gen+--+-- You can also do this using 'getSize'.+sized :: (Int -> Gen a) -> Gen a+sized f = MkGen (\r n -> let MkGen m = f n in m r n)++-- | Returns the size parameter. Used to construct generators that depend on+-- the size parameter.+--+-- For example, 'listOf', which uses the size parameter as an upper bound on+-- length of lists it generates, can be defined like this:+--+-- > listOf :: Gen a -> Gen [a]+-- > listOf gen = do+-- > n <- getSize+-- > k <- choose (0,n)+-- > vectorOf k gen+--+-- You can also do this using 'sized'.+getSize :: Gen Int+getSize = sized pure++-- | Overrides the size parameter. Returns a generator which uses+-- the given size instead of the runtime-size parameter.+resize :: WITHCALLSTACK(Int -> Gen a -> Gen a)+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.+-- For integral and enumerated types, the specialised variants of+-- 'choose' below run much quicker.+choose :: Random a => (a,a) -> Gen a+choose rng = MkGen (\r _ -> let (x,_) = randomR rng r in x)++-- | Generates a random element over the natural range of @a@.+chooseAny :: Random a => Gen a+chooseAny = MkGen (\r _ -> let (x,_) = random r in x)++-- | A fast implementation of 'choose' for enumerated types.+chooseEnum :: Enum a => (a, a) -> Gen a+chooseEnum (lo, hi) =+ fmap toEnum (chooseInt (fromEnum lo, fromEnum hi))++-- | A fast implementation of 'choose' for 'Int'.+chooseInt :: (Int, Int) -> Gen Int+chooseInt = chooseBoundedIntegral++-- Note about INLINEABLE: we specialise chooseBoundedIntegral+-- for each concrete type, so that all the bounds checks get+-- simplified away.+{-# INLINEABLE chooseBoundedIntegral #-}+-- | A fast implementation of 'choose' for bounded integral types.+chooseBoundedIntegral :: (Bounded a, Integral a) => (a, a) -> Gen a+chooseBoundedIntegral (lo, hi)+#ifndef NO_SPLITMIX+ | toInteger mn >= toInteger (minBound :: Int64) &&+ toInteger mx <= toInteger (maxBound :: Int64) =+ fmap fromIntegral (chooseInt64 (fromIntegral lo, fromIntegral hi))+ | toInteger mn >= toInteger (minBound :: Word64) &&+ toInteger mx <= toInteger (maxBound :: Word64) =+ fmap fromIntegral (chooseWord64 (fromIntegral lo, fromIntegral hi))+#endif+ | otherwise =+ fmap fromInteger (chooseInteger (toInteger lo, toInteger hi))+#ifndef NO_SPLITMIX+ where+ mn = minBound `asTypeOf` lo+ mx = maxBound `asTypeOf` hi+#endif++-- | A fast implementation of 'choose' for 'Integer'.+chooseInteger :: (Integer, Integer) -> Gen Integer+#ifdef NO_SPLITMIX+chooseInteger = choose+#else+chooseInteger (lo, hi)+ | lo >= toInteger (minBound :: Int64) && lo <= toInteger (maxBound :: Int64) &&+ hi >= toInteger (minBound :: Int64) && hi <= toInteger (maxBound :: Int64) =+ fmap toInteger (chooseInt64 (fromInteger lo, fromInteger hi))+ | lo >= toInteger (minBound :: Word64) && lo <= toInteger (maxBound :: Word64) &&+ hi >= toInteger (minBound :: Word64) && hi <= toInteger (maxBound :: Word64) =+ fmap toInteger (chooseWord64 (fromInteger lo, fromInteger hi))+ | otherwise = MkGen $ \(QCGen g) _ -> fst (nextInteger lo hi g)++chooseWord64 :: (Word64, Word64) -> Gen Word64+chooseWord64 (lo, hi)+ | lo <= hi = chooseWord64' (lo, hi)+ | otherwise = chooseWord64' (hi, lo)+ where+ chooseWord64' :: (Word64, Word64) -> Gen Word64+ chooseWord64' (lo, hi) =+ fmap (+ lo) (chooseUpTo (hi - lo))++chooseInt64 :: (Int64, Int64) -> Gen Int64+chooseInt64 (lo, hi)+ | lo <= hi = chooseInt64' (lo, hi)+ | otherwise = chooseInt64' (hi, lo)+ where+ chooseInt64' :: (Int64, Int64) -> Gen Int64+ chooseInt64' (lo, hi) = do+ w <- chooseUpTo (fromIntegral hi - fromIntegral lo)+ return (fromIntegral (w + fromIntegral lo))++chooseUpTo :: Word64 -> Gen Word64+chooseUpTo n =+ MkGen $ \(QCGen g) _ ->+ fst (bitmaskWithRejection64' n g)+#endif++-- | Run a generator. The size passed to the generator is always 30;+-- if you want another size then you should explicitly use 'resize'.+generate :: Gen a -> IO a+generate (MkGen g) =+ do r <- newQCGen+ return (g r 30)++-- | Generates some example values.+sample' :: Gen a -> IO [a]+sample' g =+ generate (sequence [ resize n g | n <- [0,2..20] ])++-- | Generates some example values and prints them to 'System.IO.stdout'.+sample :: Show a => Gen a -> IO ()+sample g =+ sequence_ [ do r <- newQCGen+ munit <- tryEvaluateIO (print $ unGen g r n)+ case munit of+ Left e+ | isDiscard e -> putStrLn "<DISCARDED>"+ | otherwise -> error $ unlines $ "Uncaught exception in sample: " : map (" " ++) (lines $ show e)+ Right () -> return ()+ | n <- [0,2..20] ]++--------------------------------------------------------------------------+-- ** Floating point++-- | Generate 'Double' in 0..1 range+genDouble :: Gen Double++-- | Generate 'Float' in 0..1 range+genFloat :: Gen Float++#ifndef NO_SPLITMIX+genDouble = MkGen $ \(QCGen g) _ -> fst (nextDouble g)+genFloat = MkGen $ \(QCGen g) _ -> fst (nextFloat g)+#else+genDouble = choose (0,1)+genFloat = choose (0,1)+#endif++--------------------------------------------------------------------------+-- ** Common generator combinators++-- | Generates a value that satisfies a predicate.+suchThat :: Gen a -> (a -> Bool) -> Gen a+gen `suchThat` p =+ do mx <- gen `suchThatMaybe` p+ case mx of+ Just x -> return x+ Nothing -> sized (\n -> resize (n+1) (gen `suchThat` p))++-- | Generates a value for which the given function returns a 'Just', and then+-- applies the function.+suchThatMap :: Gen a -> (a -> Maybe b) -> Gen b+gen `suchThatMap` f =+ fmap fromJust $ fmap f gen `suchThat` isJust++-- | Tries to generate a value that satisfies a predicate.+-- If it fails to do so after enough attempts, returns @Nothing@.+suchThatMaybe :: Gen a -> (a -> Bool) -> Gen (Maybe a)+gen `suchThatMaybe` p = sized (\n -> try n (2*n))+ where+ try m n+ | m > n = return Nothing+ | otherwise = do+ x <- resize m gen+ if p x then return (Just x) else try (m+1) n++-- | Randomly uses one of the given generators. The input list+-- must be non-empty.+oneof :: WITHCALLSTACK([Gen a] -> Gen a)+oneof [] = error "QuickCheck.oneof used with empty list"+oneof gs = chooseInt (0,length gs - 1) >>= (gs !!)++-- | Chooses one of the given generators, with a weighted random distribution.+-- The input list must be non-empty.+frequency :: WITHCALLSTACK([(Int, Gen a)] -> Gen a)+frequency [] = error "QuickCheck.frequency used with empty list"+frequency xs+ | any (< 0) (map fst xs) =+ error "QuickCheck.frequency: negative weight"+ | all (== 0) (map fst xs) =+ error "QuickCheck.frequency: all weights were zero"+frequency xs0 = chooseInt (1, tot) >>= (`pick` xs0)+ where+ tot = sum (map fst xs0)++ pick n ((k,x):xs)+ | n <= k = x+ | otherwise = pick (n-k) xs+ pick _ _ = error "QuickCheck.pick used with empty list"++-- | Generates one of the given values. The input list must be non-empty.+elements :: WITHCALLSTACK([a] -> Gen a)+elements [] = error "QuickCheck.elements used with empty list"+elements xs = (xs !!) `fmap` chooseInt (0, length xs - 1)++-- | Generates a random subsequence of the given list.+sublistOf :: [a] -> Gen [a]+sublistOf xs = filterM (\_ -> chooseEnum (False, True)) xs++-- | Generates a random permutation of the given list.+shuffle :: [a] -> Gen [a]+shuffle xs = do+ ns <- vectorOf (length xs) (chooseInt (minBound :: Int, maxBound))+ return (map snd (sortBy (comparing fst) (zip ns xs)))++-- | 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.+-- The input list must be non-empty.+growingElements :: WITHCALLSTACK([a] -> Gen a)+growingElements [] = error "QuickCheck.growingElements used with empty list"+growingElements xs = sized $ \n -> elements (take (1 `max` size n) xs)+ where+ k = length xs+ mx = 100+ log' = round . log . toDouble+ size n = (log' n + 1) * k `div` log' mx+ toDouble = fromIntegral :: Int -> Double++{- WAS:+growingElements xs = sized $ \n -> elements (take (1 `max` (n * k `div` 100)) xs)+ where+ k = length xs+-}++-- | Generates a list of random length. The maximum length depends on the+-- size parameter.+listOf :: Gen a -> Gen [a]+listOf gen = sized $ \n ->+ do k <- chooseInt (0,n)+ vectorOf k gen++-- | Generates a non-empty list of random length. The maximum length+-- depends on the size parameter.+listOf1 :: Gen a -> Gen [a]+listOf1 gen = sized $ \n ->+ do k <- chooseInt (1,1 `max` n)+ vectorOf k gen++-- | Generates a list of the given length.+vectorOf :: Int -> Gen a -> Gen [a]+vectorOf = replicateM++-- | Generates an infinite list.+infiniteListOf :: Gen a -> Gen [a]+infiniteListOf gen = sequence (repeat gen)++--------------------------------------------------------------------------+-- the end.
+ src/Test/QuickCheck/Gen/Unsafe.hs view
@@ -0,0 +1,53 @@+{-# LANGUAGE CPP #-}+#ifndef NO_SAFE_HASKELL+{-# LANGUAGE Safe #-}+#endif+#ifndef NO_ST_MONAD+{-# 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.+-- QuickCheck maintains the illusion that a 'Gen' is a probability+-- distribution and does not allow you to distinguish two generators+-- that have the same distribution.+--+-- The functions in this module allow you to break this illusion by+-- reusing the same random number seed twice. This is unsafe because+-- by applying the same seed to two morally equal generators, you can+-- see whether they are really equal or not.+module Test.QuickCheck.Gen.Unsafe where++import Test.QuickCheck.Gen+import Control.Monad+import Control.Applicative++-- | Promotes a monadic generator to a generator of monadic values.+promote :: Monad m => m (Gen a) -> Gen (m a)+promote m = flip liftM m <$> delay++-- | Randomly generates a function of type @'Gen' a -> a@, which+-- you can then use to evaluate generators. Mostly useful in+-- implementing 'promote'.+delay :: Gen (Gen a -> a)+delay = MkGen (\r n g -> unGen g r n)++#ifndef NO_ST_MONAD+-- | A variant of 'delay' that returns a polymorphic evaluation function.+-- Can be used in a pinch to generate polymorphic (rank-2) values:+--+-- > genSelector :: Gen (a -> a -> a)+-- > genSelector = elements [\x y -> x, \x y -> y]+-- >+-- > data Selector = Selector (forall a. a -> a -> a)+-- > genPolySelector :: Gen Selector+-- > genPolySelector = do+-- > Capture eval <- capture+-- > return (Selector (eval genSelector))+capture :: Gen Capture+capture = MkGen (\r n -> Capture (\g -> unGen g r n))++newtype Capture = Capture (forall a. Gen a -> a)+#endif
+ src/Test/QuickCheck/Modifiers.hs view
@@ -0,0 +1,549 @@+{-# LANGUAGE CPP #-}+#ifndef NO_SAFE_HASKELL+{-# LANGUAGE Trustworthy #-}+#endif+#ifndef NO_MULTI_PARAM_TYPE_CLASSES+{-# LANGUAGE MultiParamTypeClasses #-}+#endif+#ifndef NO_NEWTYPE_DERIVING+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+#endif+#ifndef NO_TYPEABLE+{-# LANGUAGE DeriveDataTypeable #-}+#endif+-- | Modifiers for test data.+--+-- These types do things such as restricting the kind of test data that can be generated.+-- They can be pattern-matched on in properties as a stylistic+-- alternative to using explicit quantification.+--+-- __Note__: the contents of this module are re-exported by+-- "Test.QuickCheck". You do not need to import it directly.+--+-- Examples:+--+-- @+-- -- Functions cannot be shown (but see "Test.QuickCheck.Function")+-- prop_TakeDropWhile ('Blind' p) (xs :: ['Test.QuickCheck.Poly.A']) =+-- takeWhile p xs ++ dropWhile p xs == xs+-- @+--+-- @+-- prop_TakeDrop ('NonNegative' n) (xs :: ['Test.QuickCheck.Poly.A']) =+-- take n xs ++ drop n xs == xs+-- @+--+-- @+-- -- cycle does not work for empty lists+-- prop_Cycle ('NonNegative' n) ('NonEmpty' (xs :: ['Test.QuickCheck.Poly.A'])) =+-- take n (cycle xs) == take n (xs ++ cycle xs)+-- @+--+-- @+-- -- Instead of 'Test.QuickCheck.forAll' 'orderedList'+-- prop_Sort ('Ordered' (xs :: ['Test.QuickCheck.Poly.OrdA'])) =+-- sort xs == xs+-- @+module Test.QuickCheck.Modifiers+ (+ -- ** Type-level modifiers for changing generator behavior+ Blind(..)+ , Fixed(..)+ , OrderedList(..)+ , NonEmptyList(..)+ , InfiniteList(..)+ , SortedList(..)+ , Positive(..)+ , Negative(..)+ , NonZero(..)+ , NonNegative(..)+ , NonPositive(..)+ , Large(..)+ , Small(..)+ , Smart(..)+ , Shrink2(..)+ , NoShrink(..)+#ifndef NO_MULTI_PARAM_TYPE_CLASSES+ , Shrinking(..)+ , ShrinkState(..)+#endif+ , ASCIIString(..)+ , UnicodeString(..)+ , PrintableString(..)+ )+ where++--------------------------------------------------------------------------+-- imports++import Test.QuickCheck.Gen+import Test.QuickCheck.Arbitrary+import Test.QuickCheck.Exception++import Data.List+ ( sort+ )+import Data.Ix (Ix)++#ifndef NO_TYPEABLE+import Data.Typeable (Typeable)+#endif++--------------------------------------------------------------------------+-- | @Blind x@: as x, but x does not have to be in the 'Show' class.+newtype Blind a = Blind {getBlind :: a}+ deriving ( Eq, Ord+#ifndef NO_NEWTYPE_DERIVING+ , Num, Integral, Real, Enum+#endif+#ifndef NO_TYPEABLE+ , Typeable+#endif+ )++instance Functor Blind where+ fmap f (Blind x) = Blind (f x)++instance Show (Blind a) where+ show _ = "(*)"++instance Arbitrary a => Arbitrary (Blind a) where+ arbitrary = Blind `fmap` arbitrary++ shrink (Blind x) = [ Blind x' | x' <- shrink x ]++--------------------------------------------------------------------------+-- | @Fixed x@: as x, but will not be shrunk.+newtype Fixed a = Fixed {getFixed :: a}+ deriving ( Eq, Ord, Show, Read+#ifndef NO_NEWTYPE_DERIVING+ , Num, Integral, Real, Enum+#endif+#ifndef NO_TYPEABLE+ , Typeable+#endif+ )++instance Functor Fixed where+ fmap f (Fixed x) = Fixed (f x)++instance Arbitrary a => Arbitrary (Fixed a) where+ arbitrary = Fixed `fmap` arbitrary++ -- no shrink function++--------------------------------------------------------------------------+-- | @Ordered xs@: guarantees that xs is ordered.+newtype OrderedList a = Ordered {getOrdered :: [a]}+ deriving ( Eq, Ord, Show, Read+#ifndef NO_TYPEABLE+ , Typeable+#endif+ )++instance Functor OrderedList where+ fmap f (Ordered x) = Ordered (map f x)++instance (Ord a, Arbitrary a) => Arbitrary (OrderedList a) where+ arbitrary = Ordered `fmap` orderedList++ shrink (Ordered xs) =+ [ Ordered xs'+ | xs' <- shrink xs+ , sort xs' == xs'+ ]++--------------------------------------------------------------------------+-- | @NonEmpty xs@: guarantees that xs is non-empty.+newtype NonEmptyList a = NonEmpty {getNonEmpty :: [a]}+ deriving ( Eq, Ord, Show, Read+#ifndef NO_TYPEABLE+ , Typeable+#endif+ )++instance Functor NonEmptyList where+ fmap f (NonEmpty x) = NonEmpty (map f x)++instance Arbitrary1 NonEmptyList where+ liftArbitrary = fmap NonEmpty . listOf1+ liftShrink shr = map NonEmpty . filter (not . null) . shrinkList shr . getNonEmpty++instance Arbitrary a => Arbitrary (NonEmptyList a) where+ arbitrary = arbitrary1+ shrink = shrink1++----------------------------------------------------------------------+-- | @InfiniteList xs _@: guarantees that xs is an infinite list.+-- When a counterexample is found, only prints the prefix of xs+-- that was used by the program.+--+-- Here is a contrived example property:+--+-- > prop_take_10 :: InfiniteList Char -> Bool+-- > prop_take_10 (InfiniteList xs _) =+-- > or [ x == \'a\' | x <- take 10 xs ]+--+-- In the following counterexample, the list must start with @"bbbbbbbbbb"@ but+-- the remaining (infinite) part can contain anything:+--+-- >>> quickCheck prop_take_10+-- *** Failed! Falsified (after 1 test and 14 shrinks):+-- "bbbbbbbbbb" ++ ...+data InfiniteList a =+ InfiniteList {+ getInfiniteList :: [a],+ infiniteListInternalData :: InfiniteListInternalData a }++-- Uses a similar trick to Test.QuickCheck.Function:+-- the Arbitrary instance generates an infinite list, which is+-- reduced to a finite prefix by shrinking. We use discard to+-- check that nothing coming after the finite prefix is used+-- (see infiniteListFromData).+data InfiniteListInternalData a = Infinite [a] | FinitePrefix [a]++infiniteListFromData :: InfiniteListInternalData a -> InfiniteList a+infiniteListFromData info@(Infinite xs) = InfiniteList xs info+infiniteListFromData info@(FinitePrefix xs) =+ InfiniteList (xs ++ discard) info++instance Show a => Show (InfiniteList a) where+ showsPrec _ (InfiniteList _ (Infinite _)) =+ ("<infinite list>" ++)+ showsPrec n (InfiniteList _ (FinitePrefix xs)) =+ (if n > 10 then ('(':) else id) .+ showsPrec 0 xs .+ (" ++ ..." ++) .+ (if n > 10 then (')':) else id)++instance Arbitrary a => Arbitrary (InfiniteList a) where+ arbitrary = fmap infiniteListFromData arbitrary+ shrink (InfiniteList _ info) =+ map infiniteListFromData (shrink info)++instance Arbitrary a => Arbitrary (InfiniteListInternalData a) where+ arbitrary = fmap Infinite infiniteList+ shrink (Infinite xs) =+ [FinitePrefix (take n xs) | n <- map (2^) [0..]]+ shrink (FinitePrefix xs) =+ map FinitePrefix (shrink xs)++--------------------------------------------------------------------------+-- | @Sorted xs@: guarantees that xs is sorted.+newtype SortedList a = Sorted {getSorted :: [a]}+ deriving ( Eq, Ord, Show, Read+#ifndef NO_TYPEABLE+ , Typeable+#endif+ )++instance Functor SortedList where+ fmap f (Sorted x) = Sorted (map f x)++instance (Arbitrary a, Ord a) => Arbitrary (SortedList a) where+ arbitrary = fmap (Sorted . sort) arbitrary++ shrink (Sorted xs) =+ [ Sorted xs'+ | xs' <- map sort (shrink xs)+ ]++--------------------------------------------------------------------------+-- | @Positive x@: guarantees that @x \> 0@.+newtype Positive a = Positive {getPositive :: a}+ deriving ( Eq, Ord, Show, Read+#ifndef NO_NEWTYPE_DERIVING+ , Enum+#endif+#ifndef NO_TYPEABLE+ , Typeable+#endif+ )++instance Functor Positive where+ fmap f (Positive x) = Positive (f x)++instance (Num a, Ord a, Arbitrary a) => Arbitrary (Positive a) where+ arbitrary = fmap Positive (fmap abs arbitrary `suchThat` (> 0))+ shrink (Positive x) = [ Positive x' | x' <- shrink x , x' > 0 ]++--------------------------------------------------------------------------+-- | @Negative x@: guarantees that @x \< 0@.+newtype Negative a = Negative {getNegative :: a}+ deriving ( Eq, Ord, Show, Read+#ifndef NO_NEWTYPE_DERIVING+ , Enum+#endif+#ifndef NO_TYPEABLE+ , Typeable+#endif+ )++instance Functor Negative where+ fmap f (Negative x) = Negative (f x)++instance (Num a, Ord a, Arbitrary a) => Arbitrary (Negative a) where+ arbitrary = fmap Negative (fmap (negate . abs) arbitrary `suchThat` (< 0))+ shrink (Negative x) = [ Negative x' | x' <- shrink x , x' < 0 ]++--------------------------------------------------------------------------+-- | @NonZero x@: guarantees that @x \/= 0@.+newtype NonZero a = NonZero {getNonZero :: a}+ deriving ( Eq, Ord, Show, Read+#ifndef NO_NEWTYPE_DERIVING+ , Enum+#endif+#ifndef NO_TYPEABLE+ , Typeable+#endif+ )++instance Functor NonZero where+ fmap f (NonZero x) = NonZero (f x)++instance (Num a, Eq a, Arbitrary a) => Arbitrary (NonZero a) where+ arbitrary = fmap NonZero $ arbitrary `suchThat` (/= 0)++ shrink (NonZero x) = [ NonZero x' | x' <- shrink x, x' /= 0 ]++--------------------------------------------------------------------------+-- | @NonNegative x@: guarantees that @x \>= 0@.+newtype NonNegative a = NonNegative {getNonNegative :: a}+ deriving ( Eq, Ord, Show, Read+#ifndef NO_NEWTYPE_DERIVING+ , Enum+#endif+#ifndef NO_TYPEABLE+ , Typeable+#endif+ )++instance Functor NonNegative where+ fmap f (NonNegative x) = NonNegative (f x)++instance (Num a, Ord a, Arbitrary a) => Arbitrary (NonNegative a) where+ arbitrary = fmap NonNegative (fmap abs arbitrary `suchThat` (>= 0))+ shrink (NonNegative x) = [ NonNegative x' | x' <- shrink x , x' >= 0 ]++--------------------------------------------------------------------------+-- | @NonPositive x@: guarantees that @x \<= 0@.+newtype NonPositive a = NonPositive {getNonPositive :: a}+ deriving ( Eq, Ord, Show, Read+#ifndef NO_NEWTYPE_DERIVING+ , Enum+#endif+#ifndef NO_TYPEABLE+ , Typeable+#endif+ )++instance Functor NonPositive where+ fmap f (NonPositive x) = NonPositive (f x)++instance (Num a, Ord a, Arbitrary a) => Arbitrary (NonPositive a) where+ arbitrary = fmap NonPositive (arbitrary `suchThat` (<= 0))+ shrink (NonPositive x) = [ NonPositive x' | x' <- shrink x , x' <= 0 ]++--------------------------------------------------------------------------+-- | @Large x@: by default, QuickCheck generates 'Int's drawn from a small+-- range. @Large Int@ gives you values drawn from the entire range instead.+newtype Large a = Large {getLarge :: a}+ deriving ( Eq, Ord, Show, Read+#ifndef NO_NEWTYPE_DERIVING+ , Num, Integral, Real, Enum, Ix+#endif+#ifndef NO_TYPEABLE+ , Typeable+#endif+ )++instance Functor Large where+ fmap f (Large x) = Large (f x)++instance (Integral a, Bounded a) => Arbitrary (Large a) where+ arbitrary = fmap Large arbitrarySizedBoundedIntegral+ shrink (Large x) = fmap Large (shrinkIntegral x)++--------------------------------------------------------------------------+-- | @Small x@: generates values of @x@ drawn from a small range.+-- The opposite of 'Large'.+newtype Small a = Small {getSmall :: a}+ deriving ( Eq, Ord, Show, Read+#ifndef NO_NEWTYPE_DERIVING+ , Num, Integral, Real, Enum, Ix+#endif+#ifndef NO_TYPEABLE+ , Typeable+#endif+ )++instance Functor Small where+ fmap f (Small x) = Small (f x)++instance Integral a => Arbitrary (Small a) where+ arbitrary = fmap Small arbitrarySizedIntegral+ shrink (Small x) = map Small (shrinkIntegral x)++--------------------------------------------------------------------------+-- | @Shrink2 x@: allows 2 shrinking steps at the same time when shrinking x+newtype Shrink2 a = Shrink2 {getShrink2 :: a}+ deriving ( Eq, Ord, Show, Read+#ifndef NO_NEWTYPE_DERIVING+ , Num, Integral, Real, Enum+#endif+#ifndef NO_TYPEABLE+ , Typeable+#endif+ )++instance Functor Shrink2 where+ fmap f (Shrink2 x) = Shrink2 (f x)++instance Arbitrary a => Arbitrary (Shrink2 a) where+ arbitrary =+ Shrink2 `fmap` arbitrary++ shrink (Shrink2 x) =+ [ Shrink2 y | y <- shrink_x ] +++ [ Shrink2 z+ | y <- shrink_x+ , z <- shrink y+ ]+ where+ shrink_x = shrink x++--------------------------------------------------------------------------+-- | @NoShrink x@: no shrinking+newtype NoShrink a = NoShrink {getNoShrink :: a}+ deriving ( Eq, Ord, Show, Read+#ifndef NO_NEWTYPE_DERIVING+ , Num, Integral, Real, Enum, Ix+#endif+#ifndef NO_TYPEABLE+ , Typeable+#endif+ )++instance Functor NoShrink where+ fmap f (NoShrink x) = NoShrink (f x)++instance Arbitrary a => Arbitrary (NoShrink a) where+ arbitrary = fmap NoShrink arbitrary++--------------------------------------------------------------------------+-- | @Smart _ x@: tries a different order when shrinking.+data Smart a =+ Smart Int a++instance Functor Smart where+ fmap f (Smart n x) = Smart n (f x)++instance Show a => Show (Smart a) where+ showsPrec n (Smart _ x) = showsPrec n x++instance Arbitrary a => Arbitrary (Smart a) where+ arbitrary =+ do x <- arbitrary+ return (Smart 0 x)++ shrink (Smart i x) = take i' ys `ilv` drop i' ys+ where+ ys = [ Smart j y | (j,y) <- [0..] `zip` shrink x ]+ i' = 0 `max` (i-2)++ [] `ilv` bs = bs+ as `ilv` [] = as+ (a:as) `ilv` (b:bs) = a : b : (as `ilv` bs)++{-+ shrink (Smart i x) = part0 ++ part2 ++ part1+ where+ ys = [ Smart i y | (i,y) <- [0..] `zip` shrink x ]+ i' = 0 `max` (i-2)+ k = i `div` 10++ part0 = take k ys+ part1 = take (i'-k) (drop k ys)+ part2 = drop i' ys+-}++ -- drop a (drop b xs) == drop (a+b) xs | a,b >= 0+ -- take a (take b xs) == take (a `min` b) xs+ -- take a xs ++ drop a xs == xs++ -- take k ys ++ take (i'-k) (drop k ys) ++ drop i' ys+ -- == take k ys ++ take (i'-k) (drop k ys) ++ drop (i'-k) (drop k ys)+ -- == take k ys ++ take (i'-k) (drop k ys) ++ drop (i'-k) (drop k ys)+ -- == take k ys ++ drop k ys+ -- == ys++#ifndef NO_MULTI_PARAM_TYPE_CLASSES+--------------------------------------------------------------------------+-- | @Shrinking _ x@: allows for maintaining a state during shrinking.+data Shrinking s a =+ Shrinking s a++class ShrinkState s a where+ shrinkInit :: a -> s+ shrinkState :: a -> s -> [(a,s)]++instance Functor (Shrinking s) where+ fmap f (Shrinking s x) = Shrinking s (f x)++instance Show a => Show (Shrinking s a) where+ showsPrec n (Shrinking _ x) = showsPrec n x++instance (Arbitrary a, ShrinkState s a) => Arbitrary (Shrinking s a) where+ arbitrary =+ do x <- arbitrary+ return (Shrinking (shrinkInit x) x)++ shrink (Shrinking s x) =+ [ Shrinking s' x'+ | (x',s') <- shrinkState x s+ ]++#endif /* NO_MULTI_PARAM_TYPE_CLASSES */++--------------------------------------------------------------------------+-- | @ASCIIString@: generates an ASCII string.+newtype ASCIIString = ASCIIString {getASCIIString :: String}+ deriving ( Eq, Ord, Show, Read+#ifndef NO_TYPEABLE+ , Typeable+#endif+ )++instance Arbitrary ASCIIString where+ arbitrary = ASCIIString `fmap` listOf arbitraryASCIIChar+ shrink (ASCIIString xs) = ASCIIString `fmap` shrink xs++--------------------------------------------------------------------------+-- | @UnicodeString@: generates a unicode String.+-- The string will not contain surrogate pairs.+newtype UnicodeString = UnicodeString {getUnicodeString :: String}+ deriving ( Eq, Ord, Show, Read+#ifndef NO_TYPEABLE+ , Typeable+#endif+ )++instance Arbitrary UnicodeString where+ arbitrary = UnicodeString `fmap` listOf arbitraryUnicodeChar+ shrink (UnicodeString xs) = UnicodeString `fmap` shrink xs++--------------------------------------------------------------------------+-- | @PrintableString@: generates a printable unicode String.+-- The string will not contain surrogate pairs.+newtype PrintableString = PrintableString {getPrintableString :: String}+ deriving ( Eq, Ord, Show, Read+#ifndef NO_TYPEABLE+ , Typeable+#endif+ )++instance Arbitrary PrintableString where+ arbitrary = PrintableString `fmap` listOf arbitraryPrintableChar+ shrink (PrintableString xs) = PrintableString `fmap` shrink xs++-- the end.
+ src/Test/QuickCheck/Monadic.hs view
@@ -0,0 +1,368 @@+{-# LANGUAGE CPP #-}+#ifndef NO_SAFE_HASKELL+{-# LANGUAGE Safe #-}+#endif+#ifndef NO_ST_MONAD+{-# LANGUAGE Rank2Types #-}+#endif+{-|+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+ , assertWith+ , pre+ , wp+ , pick+ , forAllM+ , monitor+ , stop++ -- * Run functions+ , monadic+ , monadic'+ , monadicIO+#ifndef NO_ST_MONAD+ , monadicST+ , runSTGen+#endif++ -- * Exceptions++#ifndef NO_EXCEPTIONS+ , assertException+ , assertExceptionIO+#ifndef NO_DEEPSEQ+ , assertDeepException+ , assertDeepExceptionIO+#endif+#endif+ ) where++--------------------------------------------------------------------------+-- imports++import Test.QuickCheck.Gen+import Test.QuickCheck.Gen.Unsafe+import Test.QuickCheck.Property++import Control.Monad(liftM, liftM2)++import Control.Monad.ST+import Control.Applicative++#ifndef NO_TRANSFORMERS+import Control.Monad.IO.Class+import Control.Monad.Trans.Class+#endif++#ifndef NO_MONADFAIL+import qualified Control.Monad.Fail as Fail+#endif++#ifndef NO_DEEPSEQ+import Control.DeepSeq+#endif++#ifndef NO_EXCEPTIONS+import qualified Control.Exception as E+#endif+--------------------------------------------------------------------------+-- 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) }++bind :: PropertyM m a -> (a -> PropertyM m b) -> PropertyM m b+MkPropertyM m `bind` f = MkPropertyM (\k -> m (\a -> unPropertyM (f a) k))++fail_ :: Monad m => String -> PropertyM m a+fail_ s = stop (failed { reason = s })++instance Functor (PropertyM m) where+ fmap f (MkPropertyM m) = MkPropertyM (\k -> m (k . f))++instance Applicative (PropertyM m) where+ pure x = MkPropertyM (\k -> k x)+ mf <*> mx =+ mf `bind` \f -> mx `bind` \x -> pure (f x)++instance Monad m => Monad (PropertyM m) where+ return = pure+ (>>=) = bind++#ifndef NO_MONADFAIL+instance Monad m => Fail.MonadFail (PropertyM m) where+ fail = fail_+#endif++#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)))++-- should think about strictness/exceptions here+-- assert :: Testable prop => prop -> PropertyM m ()+-- | Allows embedding non-monadic properties into monadic ones.+assert :: Monad m => Bool -> PropertyM m ()+assert True = return ()+assert False = fail "Assertion failed"++-- | Like 'assert' but allows caller to specify an explicit message to show on failure.+--+-- __Example:__+--+-- @+-- do+-- assertWith True "My first predicate."+-- assertWith False "My other predicate."+-- ...+-- @+--+-- @+-- Assertion failed (after 2 tests):+-- Passed: My first predicate+-- Failed: My other predicate+-- @+assertWith :: Monad m => Bool -> String -> PropertyM m ()+assertWith condition msg = do+ let prefix = if condition then "Passed: " else "Failed: "+ monitor $ counterexample $ prefix ++ msg+ assert condition++-- 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 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'.+-- __Note__: values generated by 'pick' do not shrink.+pick :: (Monad m, Show a) => Gen a -> PropertyM m a+pick gen = MkPropertyM $ \k ->+ do a <- gen+ mp <- k a+ 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++-- | Quantification in monadic properties to 'pick', with a notation similar to+-- 'forAll'. __Note__: values generated by 'forAllM' do not shrink.++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 ()))++-- run functions++monadic :: (Testable a, Monad m) => (m Property -> Property) -> PropertyM m a -> Property+monadic runner m = property (fmap runner (monadic' m))++monadic' :: (Testable a, Monad m) => PropertyM m a -> Gen (m Property)+monadic' (MkPropertyM m) = m (\prop -> return (return (property prop)))++-- | 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 :: Testable a => 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 :: Testable a => (forall s. PropertyM (ST s) a) -> Property+monadicST m = property (runSTGen (monadic' m))++runSTGen :: (forall s. Gen (ST s a)) -> Gen a+runSTGen f = do+ Capture eval <- capture+ return (runST (eval f))+#endif++-- Exceptions+++#ifndef NO_EXCEPTIONS++-- | Evaluate the value to Weak Head Normal Form (WHNF) and fail if it does not result in+-- an expected exception being thrown.+assertException ::+ E.Exception exc+ => (exc -> Bool) -- ^ Return `True` if that is the exception that was expected+ -> a -- ^ Value that should result in an exception, when evaluated to WHNF+ -> Property+assertException isExc value = assertExceptionIO isExc (return value)+++-- | Make sure that a specific exception is thrown during an IO action. The result is+-- evaluated to WHNF.+assertExceptionIO ::+ E.Exception exc+ => (exc -> Bool) -- ^ Return `True` if that is the exception that was expected+ -> IO a -- ^ An action that should throw the expected exception+ -> Property+assertExceptionIO isExc action =+ monadicIO $ do+ hasFailed <-+ run+ (E.catch+ (do res <- action+ res `seq` return False)+ (return . isExc))+ assert hasFailed++#ifndef NO_DEEPSEQ++-- | Same as `assertException`, but evaluate the value to Normal Form (NF) and fail if it+-- does not result in an expected exception being thrown.+assertDeepException ::+ (NFData a, E.Exception exc)+ => (exc -> Bool) -- ^ Return True if that is the exception that was expected+ -> a -- ^ Value that should result in an exception, when fully evaluated to NF+ -> Property+assertDeepException isExc value = assertException isExc (rnf value)++-- | Make sure that a specific exception is thrown during an IO action. The result is+-- evaluated to NF.+assertDeepExceptionIO ::+ (NFData a, E.Exception exc)+ => (exc -> Bool) -- ^ Return True if that is the exception that was expected+ -> IO a -- ^ An action that should throw the expected exception+ -> Property+assertDeepExceptionIO isExc action = assertExceptionIO isExc (fmap rnf action)++#endif+#endif++--------------------------------------------------------------------------+-- the end.
+ src/Test/QuickCheck/Monoids.hs view
@@ -0,0 +1,83 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE Safe #-}++module Test.QuickCheck.Monoids+ ( Every (..)+ , Some (..)+ ) where++#ifndef NO_SEMIGROUP+import Data.List.NonEmpty as NonEmpty+import Data.Semigroup (Semigroup (..))+#else+import Data.Monoid (Monoid (..))+#endif+import Test.QuickCheck.Property++-- | Conjunction monoid built with `.&&.`.+--+-- Use `property @Every` as an accessor which doesn't leak+-- existential variables.+--+-- Note: monoid laws are satisfied up to 'Test.QuickCheck.isSuccess' unless one+-- is using `checkCoverage`.+--+#ifndef NO_EXISTENTIAL_FIELD_SELECTORS+data Every = forall p. Testable p => Every { getEvery :: p }+#else+data Every = forall p. Testable p => Every p+#endif++instance Testable Every where+ property (Every p) = property p++#ifndef NO_SEMIGROUP+instance Semigroup Every where+ Every p <> Every p' = Every (p .&&. p')+ sconcat = Every . conjoin . NonEmpty.toList++instance Monoid Every where+ mempty = Every True+ mappend = (<>)+ mconcat = Every . conjoin+#else+instance Monoid Every where+ mempty = Every True+ mappend (Every p) (Every p') = Every (p .&&. p')+ mconcat = Every . conjoin+#endif+++-- | Disjunction monoid built with `.||.`.+--+-- Use `property @Some` as an accessor which doesn't leak+-- existential variables.+--+-- Note: monoid laws are satisfied up to 'Test.QuickCheck.isSuccess' unless one+-- is using `checkCoverage`.+--+#ifndef NO_EXISTENTIAL_FIELD_SELECTORS+data Some = forall p. Testable p => Some { getSome :: p }+#else+data Some = forall p. Testable p => Some p+#endif++instance Testable Some where+ property (Some p) = property p++#ifndef NO_SEMIGROUP+instance Semigroup Some where+ Some p <> Some p' = Some (p .||. p')+ sconcat = Some . disjoin . NonEmpty.toList++instance Monoid Some where+ mempty = Some False+ mappend = (<>)+ mconcat = Some . disjoin+#else+instance Monoid Some where+ mempty = Some False+ mappend (Some p) (Some p') = Some (p .||. p')+ mconcat = Some . disjoin+#endif
+ src/Test/QuickCheck/Poly.hs view
@@ -0,0 +1,179 @@+{-# LANGUAGE CPP #-}+#ifndef NO_SAFE_HASKELL+{-# LANGUAGE Safe #-}+#endif+-- | Types to help with testing polymorphic properties.+--+-- Types 'A', 'B' and 'C' are @newtype@ wrappers around 'Integer' that+-- implement 'Eq', 'Show', 'Arbitrary' and 'CoArbitrary'. Types+-- 'OrdA', 'OrdB' and 'OrdC' also implement 'Ord' and 'Num'.+--+-- See also "Test.QuickCheck.All" for an automatic way of testing+-- polymorphic properties.+module Test.QuickCheck.Poly+ ( A(..), B(..), C(..)+ , OrdA(..), OrdB(..), OrdC(..)+ )+ where++--------------------------------------------------------------------------+-- imports++import Test.QuickCheck.Arbitrary++--------------------------------------------------------------------------+-- polymorphic A, B, C (in Eq)++-- A++newtype A = A{ unA :: Integer }+ deriving ( Eq )++instance Show A where+ showsPrec n (A x) = showsPrec n x++instance Arbitrary A where+ arbitrary = (A . (+1) . abs) `fmap` arbitrary+ shrink (A x) = [ A x' | x' <- shrink x, x' > 0 ]++instance CoArbitrary A where+ coarbitrary = coarbitrary . unA++-- B++newtype B = B{ unB :: Integer }+ deriving ( Eq )++instance Show B where+ showsPrec n (B x) = showsPrec n x++instance Arbitrary B where+ arbitrary = (B . (+1) . abs) `fmap` arbitrary+ shrink (B x) = [ B x' | x' <- shrink x, x' > 0 ]++instance CoArbitrary B where+ coarbitrary = coarbitrary . unB++-- C++newtype C = C{ unC :: Integer }+ deriving ( Eq )++instance Show C where+ showsPrec n (C x) = showsPrec n x++instance Arbitrary C where+ arbitrary = (C . (+1) . abs) `fmap` arbitrary+ shrink (C x) = [ C x' | x' <- shrink x, x' > 0 ]++instance CoArbitrary C where+ coarbitrary = coarbitrary . unC++--------------------------------------------------------------------------+-- polymorphic OrdA, OrdB, OrdC (in Eq, Ord)++-- OrdA++newtype OrdA = OrdA{ unOrdA :: Integer }+ deriving ( Eq, Ord )++liftOrdA+ :: (Integer -> Integer)+ -> OrdA -> OrdA+liftOrdA f (OrdA x) = OrdA (f x)++liftOrdA2+ :: (Integer -> Integer -> Integer)+ -> OrdA -> OrdA -> OrdA+liftOrdA2 f (OrdA x) (OrdA y) = OrdA (f x y)++instance Num OrdA where+ (+) = liftOrdA2 (+)+ (*) = liftOrdA2 (*)+ (-) = liftOrdA2 (-)+ negate = liftOrdA negate+ abs = liftOrdA abs+ signum = liftOrdA signum+ fromInteger = OrdA . fromInteger+++instance Show OrdA where+ showsPrec n (OrdA x) = showsPrec n x++instance Arbitrary OrdA where+ arbitrary = (OrdA . (+1) . abs) `fmap` arbitrary+ shrink (OrdA x) = [ OrdA x' | x' <- shrink x, x' > 0 ]++instance CoArbitrary OrdA where+ coarbitrary = coarbitrary . unOrdA++-- OrdB++newtype OrdB = OrdB{ unOrdB :: Integer }+ deriving ( Eq, Ord )++liftOrdB+ :: (Integer -> Integer)+ -> OrdB -> OrdB+liftOrdB f (OrdB x) = OrdB (f x)++liftOrdB2+ :: (Integer -> Integer -> Integer)+ -> OrdB -> OrdB -> OrdB+liftOrdB2 f (OrdB x) (OrdB y) = OrdB (f x y)++instance Num OrdB where+ (+) = liftOrdB2 (+)+ (*) = liftOrdB2 (*)+ (-) = liftOrdB2 (-)+ negate = liftOrdB negate+ abs = liftOrdB abs+ signum = liftOrdB signum+ fromInteger = OrdB . fromInteger++instance Show OrdB where+ showsPrec n (OrdB x) = showsPrec n x++instance Arbitrary OrdB where+ arbitrary = (OrdB . (+1) . abs) `fmap` arbitrary+ shrink (OrdB x) = [ OrdB x' | x' <- shrink x, x' > 0 ]++instance CoArbitrary OrdB where+ coarbitrary = coarbitrary . unOrdB++-- OrdC++newtype OrdC = OrdC{ unOrdC :: Integer }+ deriving ( Eq, Ord )++liftOrdC+ :: (Integer -> Integer)+ -> OrdC -> OrdC+liftOrdC f (OrdC x) = OrdC (f x)++liftOrdC2+ :: (Integer -> Integer -> Integer)+ -> OrdC -> OrdC -> OrdC+liftOrdC2 f (OrdC x) (OrdC y) = OrdC (f x y)++instance Num OrdC where+ (+) = liftOrdC2 (+)+ (*) = liftOrdC2 (*)+ (-) = liftOrdC2 (-)+ negate = liftOrdC negate+ abs = liftOrdC abs+ signum = liftOrdC signum+ fromInteger = OrdC . fromInteger++instance Show OrdC where+ showsPrec n (OrdC x) = showsPrec n x++instance Arbitrary OrdC where+ arbitrary = (OrdC . (+1) . abs) `fmap` arbitrary+ shrink (OrdC x) = [ OrdC x' | x' <- shrink x, x' > 0 ]++instance CoArbitrary OrdC where+ coarbitrary = coarbitrary . unOrdC++--------------------------------------------------------------------------+-- the end.
+ src/Test/QuickCheck/Property.hs view
@@ -0,0 +1,1099 @@+{-# OPTIONS_HADDOCK hide #-}+-- | Combinators for constructing properties.+{-# LANGUAGE CPP #-}+#ifndef NO_TYPEABLE+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE ExistentialQuantification #-}+#endif+#ifndef NO_SAFE_HASKELL+{-# LANGUAGE Safe #-}+#endif+module Test.QuickCheck.Property where++--------------------------------------------------------------------------+-- imports++import Test.QuickCheck.Gen+import Test.QuickCheck.Gen.Unsafe+import Test.QuickCheck.Arbitrary+import Test.QuickCheck.Text( isOneLine, putLine )+import Test.QuickCheck.Exception+import Test.QuickCheck.State( State(terminal, numSuccessTests, numDiscardedTests, maxSuccessTests, numSuccessShrinks, numTryShrinks, numTotTryShrinks), Confidence(..), TestProgress(..) )++#ifndef NO_TIMEOUT+import System.Timeout(timeout)+#endif+import Data.Maybe+import Control.Applicative+#if defined(MIN_VERSION_base)+import Control.Exception (displayException)+#endif+import Control.Monad+import qualified Data.Map as Map+import Data.Map(Map)+import qualified Data.Set as Set+import Data.Set(Set)+#ifndef NO_DEEPSEQ+import Control.DeepSeq+#endif+#ifndef NO_TYPEABLE+import Data.Typeable (Typeable, cast)+#endif+import Data.Maybe++--------------------------------------------------------------------------+-- fixities++infixr 0 ==>+infixr 1 .&.+infixr 1 .&&.+infixr 1 .||.++-- The story for exception handling:+--+-- To avoid insanity, we have rules about which terms can throw+-- exceptions when we evaluate them:+-- * A rose tree must evaluate to WHNF without throwing an exception+-- * The 'ok' component of a Result must evaluate to Just True or+-- Just False or Nothing rather than raise an exception+-- * IORose _ must never throw an exception when executed+--+-- Both rose trees and Results may loop when we evaluate them, though,+-- so we have to be careful not to force them unnecessarily.+--+-- We also have to be careful when we use fmap or >>= in the Rose+-- monad that the function we supply is total, or else use+-- protectResults afterwards to install exception handlers. The+-- mapResult function on Properties installs an exception handler for+-- us, though.+--+-- Of course, the user is free to write "error "ha ha" :: Result" if+-- they feel like it. We have to make sure that any user-supplied Rose+-- Results or Results get wrapped in exception handlers, which we do by:+-- * Making the 'property' function install an exception handler+-- round its argument. This function always gets called in the+-- right places, because all our Property-accepting functions are+-- actually polymorphic over the Testable class so they have to+-- call 'property'.+-- * Installing an exception handler round a Result before we put it+-- in a rose tree (the only place Results can end up).++--------------------------------------------------------------------------+-- * Property and Testable types++-- | The type of properties.+newtype Property = MkProperty { unProperty :: Gen Prop }+#ifndef NO_TYPEABLE+ deriving (Typeable)+#endif++-- | The class of properties, i.e., types which QuickCheck knows how to test.+-- Typically a property will be a function returning 'Bool' or 'Property'.+class Testable prop where+ -- | Convert the thing to a property.+ property :: prop -> Property++ -- | Optional; used internally in order to improve shrinking.+ -- Tests a property but also quantifies over an extra value+ -- (with a custom shrink and show function).+ -- The 'Testable' instance for functions defines+ -- @propertyForAllShrinkShow@ in a way that improves shrinking.+ propertyForAllShrinkShow :: Gen a -> (a -> [a]) -> (a -> [String]) -> (a -> prop) -> Property+ propertyForAllShrinkShow gen shr shw f =+ forAllShrinkBlind gen shr $+ \x -> foldr counterexample (property (f x)) (shw x)++-- | If a property returns 'Discard', the current test case is discarded,+-- the same as if a precondition was false.+--+-- An example is the definition of '==>':+--+-- > (==>) :: Testable prop => Bool -> prop -> Property+-- > False ==> _ = property Discard+-- > True ==> p = property p+data Discard = Discard++instance Testable Discard where+ property _ = property rejected++-- This instance is here to make it easier to turn IO () into a Property.+instance Testable () where+ property = property . liftUnit+ where+ -- N.B. the unit gets forced only inside 'property',+ -- so that we turn exceptions into test failures+ liftUnit () = succeeded++instance Testable prop => Testable (Maybe prop) where+ property = property . liftMaybe+ where+ -- See comment for liftUnit above+ liftMaybe Nothing = property Discard+ liftMaybe (Just prop) = property prop++instance Testable Bool where+ property = property . liftBool++instance Testable Result where+ property = MkProperty . return . MkProp . protectResults . return++instance Testable Prop where+ property p = MkProperty . return . protectProp $ p++instance Testable prop => Testable (Gen prop) where+ property mp = MkProperty $ do p <- mp; unProperty (property p)++instance Testable Property where+ property (MkProperty mp) = MkProperty (fmap protectProp mp)++-- | Do I/O inside a property.+{-# DEPRECATED morallyDubiousIOProperty "Use 'ioProperty' instead" #-}+morallyDubiousIOProperty :: Testable prop => IO prop -> Property+morallyDubiousIOProperty = ioProperty++-- | Do I/O inside a property.+--+-- Warning: any random values generated inside of the argument to @ioProperty@+-- will not currently be shrunk. For best results, generate all random values+-- before calling @ioProperty@, or use 'idempotentIOProperty' if that is safe.+ioProperty :: Testable prop => IO prop -> Property+ioProperty prop = idempotentIOProperty (fmap noShrinking prop)++-- | Do I/O inside a property.+--+-- Warning: during shrinking, the I/O may not always be re-executed.+-- Instead, the I/O may be executed once and then its result retained.+-- If this is not acceptable, use 'ioProperty' instead.+idempotentIOProperty :: Testable prop => IO prop -> Property+idempotentIOProperty =+ MkProperty . fmap (MkProp . ioRose . fmap unProp) .+ promote . fmap (unProperty . property)++instance (Arbitrary a, Show a, Testable prop) => Testable (a -> prop) where+ property f =+ propertyForAllShrinkShow arbitrary shrink (return . show) f+ propertyForAllShrinkShow gen shr shw f =+ -- gen :: Gen b, shr :: b -> [b], f :: b -> a -> prop+ -- Idea: Generate and shrink (b, a) as a pair+ propertyForAllShrinkShow+ (liftM2 (,) gen arbitrary)+ (liftShrink2 shr shrink)+ (\(x, y) -> shw x ++ [show y])+ (uncurry f)++-- ** Exception handling+protect :: (AnException -> a) -> IO a -> IO a+protect f x = either f id `fmap` tryEvaluateIO x++--------------------------------------------------------------------------+-- ** Type Prop++newtype Prop = MkProp{ unProp :: Rose Result }++-- ** type Rose++data Rose a = MkRose a [Rose a] | IORose (IO (Rose a))+-- Only use IORose if you know that the argument is not going to throw an exception!+-- Otherwise, try ioRose.+ioRose :: IO (Rose Result) -> Rose Result+ioRose = IORose . protectRose++joinRose :: Rose (Rose a) -> Rose a+joinRose (IORose rs) = IORose (fmap joinRose rs)+joinRose (MkRose (IORose rm) rs) = IORose $ do r <- rm; return (joinRose (MkRose r rs))+joinRose (MkRose (MkRose x ts) tts) =+ -- first shrinks outer quantification; makes most sense+ MkRose x (map joinRose tts ++ ts)+ -- first shrinks inner quantification: terrible+ --MkRose x (ts ++ map joinRose tts)++instance Functor Rose where+ -- f must be total+ fmap f (IORose rs) = IORose (fmap (fmap f) rs)+ fmap f (MkRose x rs) = MkRose (f x) [ fmap f r | r <- rs ]++instance Applicative Rose where+ pure x = MkRose x []+ -- f must be total+ (<*>) = liftM2 ($)++instance Monad Rose where+ return = pure+ -- k must be total+ m >>= k = joinRose (fmap k m)++-- | 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.+-- 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.+protectRose :: IO (Rose Result) -> IO (Rose Result)+protectRose = protect (return . exception "Exception")++-- | Wrap the top level of a 'Prop' in an exception handler.+protectProp :: Prop -> Prop+protectProp (MkProp r) = MkProp (IORose . protectRose . return $ r)++-- | Wrap all the Results in a rose tree in exception handlers.+protectResults :: Rose Result -> Rose Result+protectResults = onRose $ \x rs ->+ IORose $ do+ y <- protectResult (return x)+ return (MkRose y (map protectResults rs))++-- ** Result type++-- | Different kinds of callbacks+data Callback+ = PostTest CallbackKind (State -> Result -> IO ()) -- ^ Called just after a test+ | PostFinalFailure CallbackKind (State -> Result -> IO ()) -- ^ Called with the final failing test-case+data CallbackKind = Counterexample -- ^ Affected by the 'verbose' combinator+ | NotCounterexample -- ^ Not affected by the 'verbose' combinator++#ifndef NO_TYPEABLE+data Witness = forall a. (Typeable a, Show a) => Wit a++instance Show Witness where+ show (Wit a) = show a++coerceWitness :: Typeable a => Witness -> a+coerceWitness (Wit a) = case cast a of+ Nothing -> error $ "Can't coerceWitness " ++ show a+ Just a -> a++castWitness :: Typeable a => Witness -> Maybe a+castWitness (Wit a) = cast a++#define WITNESSES(a) , theWitnesses a+#else+#define WITNESSES(a)+#endif++-- | The result of a single test.+data Result+ = MkResult+ { ok :: Maybe Bool+ -- ^ result of the test case; Nothing = discard+ , expect :: Bool+ -- ^ indicates what the expected result of the property is+ , 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+ , maybeNumTests :: Maybe Int+ -- ^ stop after this many tests+ , maybeCheckCoverage :: Maybe Confidence+ -- ^ required coverage confidence+ , maybeDiscardedRatio :: Maybe Int+ -- ^ maximum number of discarded tests per successful test+ , maybeMaxShrinks :: Maybe Int+ -- ^ maximum number of shrinks+ , maybeMaxTestSize :: Maybe Int+ -- ^ maximum test size+ , labels :: [String]+ -- ^ test case labels+ , classes :: [(String, Bool)]+ -- ^ test case classes+ , tables :: [(String, String)]+ -- ^ test case tables+ , requiredCoverage :: [(Maybe String, String, Double)]+ -- ^ required coverage+ , callbacks :: [Callback]+ -- ^ the callbacks for this test case+ , testCase :: [String]+ -- ^ the generated test case+ WITNESSES(:: [Witness])+ }++exception :: String -> AnException -> Result+exception msg err+ | isDiscard err = rejected+ | otherwise = failed{ reason = formatException msg err,+ theException = Just err }++formatException :: String -> AnException -> String+#if defined(MIN_VERSION_base)+formatException msg err = msg ++ ":" ++ format (displayException err)+#else+formatException msg err = msg ++ ":" ++ format (show err)+#endif+ where format xs | isOneLine xs = " '" ++ xs ++ "'"+ | otherwise = "\n" ++ unlines [ " " ++ l | l <- lines xs ]++protectResult :: IO Result -> IO Result+protectResult = protect (exception "Exception")++succeeded, failed, rejected :: Result+(succeeded, failed, rejected) =+ (result{ ok = Just True },+ result{ ok = Just False },+ result{ ok = Nothing })+ where+ result =+ MkResult+ { ok = undefined+ , expect = True+ , reason = ""+ , theException = Nothing+ , abort = False+ , maybeNumTests = Nothing+ , maybeCheckCoverage = Nothing+ , maybeDiscardedRatio = Nothing+ , maybeMaxShrinks = Nothing+ , maybeMaxTestSize = Nothing+ , labels = []+ , classes = []+ , tables = []+ , requiredCoverage = []+ , callbacks = []+ , testCase = []+ WITNESSES(= [])+ }++--------------------------------------------------------------------------+-- ** Lifting and mapping functions++liftBool :: Bool -> Result+liftBool True = succeeded+liftBool False = failed { reason = "Falsified" }++mapResult :: Testable prop => (Result -> Result) -> prop -> Property+mapResult f = mapRoseResult (protectResults . fmap f)++mapTotalResult :: Testable prop => (Result -> Result) -> prop -> Property+mapTotalResult f = mapRoseResult (fmap f)++-- f here mustn't throw an exception (rose tree invariant).+mapRoseResult :: Testable prop => (Rose Result -> Rose Result) -> prop -> Property+mapRoseResult f = mapProp (\(MkProp t) -> MkProp (f t))++mapProp :: Testable prop => (Prop -> Prop) -> prop -> Property+mapProp f = MkProperty . fmap f . unProperty . property++--------------------------------------------------------------------------+-- ** Property combinators++-- | Adjust the test case size for a property, by transforming it with the given+-- function.+mapSize :: Testable prop => (Int -> Int) -> prop -> Property+mapSize f = property . scale f . unProperty . property++-- | Shrinks the argument to a property if it fails. Shrinking is done+-- automatically for most types. This function is only needed when you want to+-- override the default behavior.+shrinking :: Testable prop =>+ (a -> [a]) -- ^ 'shrink'-like function.+ -> a -- ^ The original argument+ -> (a -> prop) -> Property+shrinking shrinker x0 pf = MkProperty (fmap (MkProp . joinRose . fmap unProp) (promote (props x0)))+ where+ props x =+ MkRose (unProperty (property (pf x))) [ props x' | x' <- shrinker x ]++-- | Disables shrinking for a property altogether.+-- Only quantification /inside/ the call to 'noShrinking' is affected.+noShrinking :: Testable prop => prop -> Property+noShrinking = mapRoseResult (onRose (\res _ -> MkRose res []))++-- | Adds a callback+callback :: Testable prop => Callback -> prop -> Property+callback cb = mapTotalResult (\res -> res{ callbacks = cb : callbacks res })++-- | Adds the given string to the counterexample if the property fails.+counterexample :: Testable prop => String -> prop -> Property+counterexample s =+ mapTotalResult (\res -> res{ testCase = s:testCase res }) .+ callback (PostFinalFailure Counterexample $ \st _res -> do+ s <- showCounterexample s+ putLine (terminal st) s)++showCounterexample :: String -> IO String+showCounterexample s = do+ let force [] = return ()+ force (x:xs) = x `seq` force xs+ res <- tryEvaluateIO (force s)+ return $+ case res of+ Left err ->+ formatException "Exception thrown while showing test case" err+ Right () ->+ s++-- | Adds the given string to the counterexample if the property fails.+{-# DEPRECATED printTestCase "Use counterexample instead" #-}+printTestCase :: Testable prop => String -> prop -> Property+printTestCase = counterexample++-- | Performs an 'IO' action after the last failure of a property.+whenFail :: Testable prop => IO () -> prop -> Property+whenFail m =+ callback $ PostFinalFailure NotCounterexample $ \_st _res ->+ m++-- | Performs an 'IO' action every time a property fails. Thus,+-- if shrinking is done, this can be used to keep track of the+-- failures along the way.+whenFail' :: Testable prop => IO () -> prop -> Property+whenFail' m =+ callback $ PostTest NotCounterexample $ \_st res ->+ if ok res == Just False+ then m+ else return ()++-- | Performs an IO action every time a property is tested, after every test.+-- The IO action is allowed to depend on @TestProgress@, which contains information+-- regarding how testing is progressing.+--+-- Note: QC invokes callbacks before the internal state has been updated to reflect the+-- most recent test. The means that e.g. @currentPassed@ will, after the first test has+-- been executed, still show 0.+withProgress :: Testable prop => (TestProgress -> IO ()) -> prop -> Property+withProgress m =+ callback $ PostTest NotCounterexample $ \st _r ->+ let tp = TestProgress { currentPassed = numSuccessTests st+ , currentDiscarded = numDiscardedTests st+ , maxTests = maxSuccessTests st+ , currentShrinks = numSuccessShrinks st+ , currentFailedShrinks = numTryShrinks st+ , currentTotalShrinks = numTotTryShrinks st+ }+ in m tp++-- | Prints out the generated test case every time the property is tested.+-- Only variables quantified over /inside/ the 'verbose' are printed.+--+-- Note: for technical reasons, the test case is printed out /after/+-- the property is tested. To debug a property that goes into an+-- infinite loop, use 'within' to add a timeout instead.+verbose :: Testable prop => prop -> Property+verbose = mapResult (\res -> res { callbacks = newCallback (callbacks res):callbacks res })+ where newCallback cbs =+ PostTest Counterexample $ \st res -> do+ putLine (terminal st) (status res ++ ":")+ sequence_ [ f st res | PostFinalFailure Counterexample f <- cbs ]+ putLine (terminal st) ""+ status MkResult{ok = Just True} = "Passed"+ status MkResult{ok = Just False} = "Failed"+ status MkResult{ok = Nothing} = "Skipped (precondition false)"++-- | Prints out the generated test case every time the property fails, including during shrinking.+-- Only variables quantified over /inside/ the 'verboseShrinking' are printed.+--+-- Note: for technical reasons, the test case is printed out /after/+-- the property is tested. To debug a property that goes into an+-- infinite loop, use 'within' to add a timeout instead.+verboseShrinking :: Testable prop => prop -> Property+verboseShrinking = mapResult (\res -> res { callbacks = newCallback (callbacks res):callbacks res })+ where newCallback cbs =+ PostTest Counterexample $ \st res ->+ when (ok res == Just False) $ do+ putLine (terminal st) "Failed:"+ sequence_ [ f st res | PostFinalFailure Counterexample f <- cbs ]+ putLine (terminal st) ""++-- | Indicates that a property is supposed to fail.+-- QuickCheck will report an error if it does not fail.+expectFailure :: Testable prop => prop -> Property+expectFailure = mapTotalResult (\res -> res{ expect = False })++-- | Modifies a property so that it only will be tested once.+-- Opposite of 'again'.+once :: Testable prop => prop -> Property+once = mapTotalResult (\res -> res{ abort = True })++-- | Modifies a property so that it will be tested repeatedly.+-- Opposite of 'once'.+again :: Testable prop => prop -> Property+again = mapTotalResult (\res -> res{ abort = False })++-- | Configures how many times a property will be tested.+--+-- For example,+--+-- > quickCheck (withMaxSuccess 1000 p)+--+-- will test @p@ up to 1000 times.+{-# DEPRECATED withMaxSuccess "Use withNumTests instead" #-}+withMaxSuccess :: Testable prop => Int -> prop -> Property+withMaxSuccess = withNumTests++-- | Configures how many times a property will be tested.+--+-- For example,+--+-- > quickCheck (withNumTests 1000 p)+--+-- will test @p@ up to 1000 times.+withNumTests :: Testable prop => Int -> prop -> Property+withNumTests n = n `seq` mapTotalResult (\res -> res{ maybeNumTests = Just n })++-- | Configures how many times a property is allowed to be discarded before failing.+--+-- For example,+--+-- > quickCheck (withDiscardRatio 10 p)+--+-- will allow @p@ to fail up to 10 times per successful test.+withDiscardRatio :: Testable prop => Int -> prop -> Property+withDiscardRatio n = n `seq` mapTotalResult (\res -> res{ maybeDiscardedRatio = Just n })++-- | Configure the maximum number of times a property will be shrunk.+--+-- For example,+--+-- > quickCheck (withMaxShrinks 100 p)+--+-- will cause @p@ to only attempt 100 shrinks on failure.+withMaxShrinks :: Testable prop => Int -> prop -> Property+withMaxShrinks n = n `seq` mapTotalResult (\res -> res{ maybeMaxShrinks = Just n })++-- | Configure the maximum size a property will be tested at.+withMaxSize :: Testable prop => Int -> prop -> Property+withMaxSize n = n `seq` mapTotalResult (\res -> res{ maybeMaxTestSize = Just n })++#ifndef NO_TYPEABLE+-- | Return a value in the 'Test.QuickCheck.witnesses' field of the 'Result'+-- returned by 'Test.QuickCheck.quickCheckResult'. Witnesses are returned+-- outer-most first.+--+-- In ghci, for example:+--+-- >>> [Wit x] <- fmap witnesses . quickCheckResult $ \ x -> witness x $ x == (0 :: Int)+-- *** Failed! Falsified (after 2 tests):+-- 1+-- >>> x+-- 1+-- >>> :t x+-- x :: Int+witness :: (Typeable a, Show a, Testable prop) => a -> prop -> Property+witness a = a `seq` mapTotalResult (\res -> res{ theWitnesses = Wit a : theWitnesses res })+#endif++-- | Check that all coverage requirements defined by 'cover' and 'coverTable'+-- are met, using a statistically sound test, and fail if they are not met.+--+-- Ordinarily, a failed coverage check does not cause the property to fail.+-- This is because the coverage requirement is not tested in a statistically+-- sound way. If you use 'cover' to express that a certain value must appear 20%+-- of the time, QuickCheck will warn you if the value only appears in 19 out of+-- 100 test cases - but since the coverage varies randomly, you may have just+-- been unlucky, and there may not be any real problem with your test+-- generation.+--+-- When you use 'checkCoverage', QuickCheck uses a statistical test to account+-- for the role of luck in coverage failures. It will run as many tests as+-- needed until it is sure about whether the coverage requirements are met. If a+-- coverage requirement is not met, the property fails.+--+-- Example:+--+-- > quickCheck (checkCoverage prop_foo)+checkCoverage :: Testable prop => prop -> Property+checkCoverage = checkCoverageWith stdConfidence++-- | Check coverage requirements using a custom confidence level.+-- See 'stdConfidence'.+--+-- An example of making the statistical test less stringent in order to improve+-- performance:+--+-- > quickCheck (checkCoverageWith stdConfidence{certainty = 10^6} prop_foo)+checkCoverageWith :: Testable prop => Confidence -> prop -> Property+checkCoverageWith confidence =+ certainty confidence `seq`+ tolerance confidence `seq`+ mapTotalResult (\res -> res{ maybeCheckCoverage = Just confidence })++-- | The standard parameters used by 'checkCoverage': @certainty = 10^9@,+-- @tolerance = 0.9@. See 'Confidence' for the meaning of the parameters.+stdConfidence :: Confidence+stdConfidence =+ Confidence {+ certainty = 10^9,+ tolerance = 0.9 }++-- | Attaches a label to a test case. This is used for reporting+-- test case distribution.+--+-- For example:+--+-- > prop_reverse_reverse :: [Int] -> Property+-- > prop_reverse_reverse xs =+-- > label ("length of input is " ++ show (length xs)) $+-- > reverse (reverse xs) === xs+--+-- >>> quickCheck prop_reverse_reverse+-- +++ OK, passed 100 tests:+-- 7% length of input is 7+-- 6% length of input is 3+-- 5% length of input is 4+-- 4% length of input is 6+-- ...+--+-- Each use of 'label' in your property results in a separate+-- table of test case distribution in the output. If this is+-- not what you want, use 'tabulate'.+label :: Testable prop => String -> prop -> Property+label s =+#ifndef NO_DEEPSEQ+ s `deepseq`+#endif+ mapTotalResult $+ \res -> res { labels = s:labels res }++-- | Attaches a label to a test case. This is used for reporting+-- test case distribution.+--+-- > collect x = label (show x)+--+-- For example:+--+-- > prop_reverse_reverse :: [Int] -> Property+-- > prop_reverse_reverse xs =+-- > collect (length xs) $+-- > reverse (reverse xs) === xs+--+-- >>> quickCheck prop_reverse_reverse+-- +++ OK, passed 100 tests:+-- 7% 7+-- 6% 3+-- 5% 4+-- 4% 6+-- ...+--+-- Each use of 'collect' in your property results in a separate+-- table of test case distribution in the output. If this is+-- not what you want, use 'tabulate'.+collect :: (Show a, Testable prop) => a -> prop -> Property+collect x = label (show x)++-- | Reports how many test cases satisfy a given condition.+--+-- For example:+--+-- > prop_sorted_sort :: [Int] -> Property+-- > prop_sorted_sort xs =+-- > sorted xs ==>+-- > classify (length xs > 1) "non-trivial" $+-- > sort xs === xs+--+-- >>> quickCheck prop_sorted_sort+-- +++ OK, passed 100 tests (22% non-trivial).+classify :: Testable prop =>+ Bool -- ^ @True@ if the test case should be labelled.+ -> String -- ^ Label.+ -> prop -> Property+classify b s =+#ifndef NO_DEEPSEQ+ s `deepseq`+#endif+ b `seq`+ mapTotalResult $+ \res -> res { classes = (s, b):classes res }++-- | 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.+--+-- __Note:__ If the coverage check fails, QuickCheck prints out a warning, but+-- the property does /not/ fail. To make the property fail, use 'checkCoverage'.+--+-- For example:+--+-- > prop_sorted_sort :: [Int] -> Property+-- > prop_sorted_sort xs =+-- > sorted xs ==>+-- > cover 50 (length xs > 1) "non-trivial" $+-- > sort xs === xs+--+-- >>> quickCheck prop_sorted_sort+-- +++ OK, passed 100 tests; 135 discarded (26% non-trivial).+-- <BLANKLINE>+-- Only 26% non-trivial, but expected 50%+cover :: Testable prop =>+ Double -- ^ The required percentage (0-100) of test cases.+ -> Bool -- ^ @True@ if the test case belongs to the class.+ -> String -- ^ Label for the test case class.+ -> prop -> Property+cover p x s = mapTotalResult f . classify x s+ where+ f res = res { requiredCoverage = (Nothing, s, p/100):requiredCoverage res }++-- | Collects information about test case distribution into a table.+-- The arguments to 'tabulate' are the table's name and a list of values+-- associated with the current test case. After testing, QuickCheck prints the+-- frequency of all collected values. The frequencies are expressed as a+-- percentage of the total number of values collected.+--+-- You should prefer 'tabulate' to 'label' when each test case is associated+-- with a varying number of values. Here is a (not terribly useful) example,+-- where the test data is a list of integers and we record all values that+-- occur in the list:+--+-- > prop_sorted_sort :: [Int] -> Property+-- > prop_sorted_sort xs =+-- > sorted xs ==>+-- > tabulate "List elements" (map show xs) $+-- > sort xs === xs+--+-- >>> quickCheck prop_sorted_sort+-- +++ OK, passed 100 tests; 1684 discarded.+-- <BLANKLINE>+-- List elements (109 in total):+-- 3.7% 0+-- 3.7% 17+-- 3.7% 2+-- 3.7% 6+-- 2.8% -6+-- 2.8% -7+--+-- Here is a more useful example. We are testing a chatroom, where the user can+-- log in, log out, or send a message:+--+-- > data Command = LogIn | LogOut | SendMessage String deriving (Data, Show)+-- > instance Arbitrary Command where ...+--+-- There are some restrictions on command sequences; for example, the user must+-- log in before doing anything else. The function @valid :: [Command] -> Bool@+-- checks that a command sequence is allowed. Our property then has the form:+--+-- > prop_chatroom :: [Command] -> Property+-- > prop_chatroom cmds =+-- > valid cmds ==>+-- > ...+--+-- The use of '==>' may skew test case distribution. We use 'collect' to see the+-- length of the command sequences, and 'tabulate' to get the frequencies of the+-- individual commands:+--+-- > prop_chatroom :: [Command] -> Property+-- > prop_chatroom cmds =+-- > wellFormed cmds LoggedOut ==>+-- > 'collect' (length cmds) $+-- > 'tabulate' "Commands" (map (show . 'Data.Data.toConstr') cmds) $+-- > ...+--+-- >>> quickCheckWith stdArgs{maxDiscardRatio = 1000} prop_chatroom+-- +++ OK, passed 100 tests; 2775 discarded:+-- 60% 0+-- 20% 1+-- 15% 2+-- 3% 3+-- 1% 4+-- 1% 5+-- <BLANKLINE>+-- Commands (68 in total):+-- 62% LogIn+-- 22% SendMessage+-- 16% LogOut+tabulate :: Testable prop => String -> [String] -> prop -> Property+tabulate key values =+#ifndef NO_DEEPSEQ+ key `deepseq` values `deepseq`+#endif+ mapTotalResult $+ \res -> res { tables = [(key, value) | value <- values] ++ tables res }++-- | Checks that the values in a given @table@ appear a certain proportion of+-- the time. A call to 'coverTable' @table@ @[(x1, p1), ..., (xn, pn)]@ asserts+-- that of the values in @table@, @x1@ should appear at least @p1@ percent of+-- the time that @table@ appears, @x2@ at least @p2@ percent of the time that+-- @table@ appears, and so on.+--+-- __Note:__ If the coverage check fails, QuickCheck prints out a warning, but+-- the property does /not/ fail. To make the property fail, use 'checkCoverage'.+--+-- Continuing the example from the 'tabulate' combinator...+--+-- > data Command = LogIn | LogOut | SendMessage String deriving (Data, Show)+-- > prop_chatroom :: [Command] -> Property+-- > prop_chatroom cmds =+-- > wellFormed cmds LoggedOut ==>+-- > 'tabulate' "Commands" (map (show . 'Data.Data.toConstr') cmds) $+-- > ...+--+-- ...we can add a coverage requirement as follows, which checks that @LogIn@,+-- @LogOut@ and @SendMessage@ each occur at least 25% of the time:+--+-- > prop_chatroom :: [Command] -> Property+-- > prop_chatroom cmds =+-- > wellFormed cmds LoggedOut ==>+-- > coverTable "Commands" [("LogIn", 25), ("LogOut", 25), ("SendMessage", 25)] $+-- > 'tabulate' "Commands" (map (show . 'Data.Data.toConstr') cmds) $+-- > ... property goes here ...+--+-- >>> quickCheck prop_chatroom+-- +++ OK, passed 100 tests; 2909 discarded:+-- 56% 0+-- 17% 1+-- 10% 2+-- 6% 3+-- 5% 4+-- 3% 5+-- 3% 7+-- <BLANKLINE>+-- Commands (111 in total):+-- 51.4% LogIn+-- 30.6% SendMessage+-- 18.0% LogOut+-- <BLANKLINE>+-- Table 'Commands' had only 18.0% LogOut, but expected 25.0%+coverTable :: Testable prop =>+ String -> [(String, Double)] -> prop -> Property+coverTable table xs =+#ifndef NO_DEEPSEQ+ table `deepseq` xs `deepseq`+#endif+ mapTotalResult $+ \res -> res { requiredCoverage = ys ++ requiredCoverage res }+ where+ ys = [(Just table, x, p/100) | (x, p) <- xs]++-- | Implication for properties: The resulting property holds if+-- the first argument is 'False' (in which case the test case is discarded),+-- or if the given property holds. Note that using implication carelessly can+-- severely skew test case distribution: consider using 'cover' to make sure+-- that your test data is still good quality.+(==>) :: Testable prop => Bool -> prop -> Property+False ==> _ = property Discard+True ==> p = property p++-- | Considers a property failed if it does not complete within+-- the given number of microseconds.+--+-- Note: if the property times out, variables quantified inside the+-- `within` will not be printed. Therefore, you should use `within`+-- only in the body of your property.+--+-- Good: @prop_foo a b c = within 1000000 ...@+--+-- Bad: @prop_foo = within 1000000 $ \\a b c -> ...@+--+-- Bad: @prop_foo a b c = ...; main = quickCheck (within 1000000 prop_foo)@+within :: Testable prop => Int -> prop -> Property+within n = onTimeout+ (failed { reason = "Timeout of " ++ show n ++ " microseconds exceeded." })+ n++-- | Discards the test case if it does not complete within the given+-- number of microseconds. This can be useful when testing algorithms+-- that have pathological cases where they run extremely slowly.+discardAfter :: Testable prop => Int -> prop -> Property+discardAfter n = onTimeout+ (rejected { reason = "Timeout of " ++ show n ++ " microseconds exceeded." })+ n++onTimeout :: Testable prop => Result -> Int -> prop -> Property+onTimeout timeoutResult n = mapRoseResult f+ where+ f rose = ioRose $ do+ let orError :: IO (Maybe a) -> a -> IO a+ m `orError` x = fmap (fromMaybe x) m+ MkRose res roses <- timeout n (reduceRose rose) `orError`+ return timeoutResult+ res' <- timeout n (protectResult (return res)) `orError`+ timeoutResult+ return (MkRose res' (map f roses))+#ifdef NO_TIMEOUT+ timeout _ = fmap Just+#endif++++-- | Explicit universal quantification: uses an explicitly given+-- test case generator.+forAll :: (Show a, Testable prop)+ => Gen a -> (a -> prop) -> Property+forAll gen pf = forAllShrink gen (\_ -> []) pf++-- | Like 'forAll', but with an explicitly given show function.+forAllShow :: Testable prop+ => Gen a -> (a -> String) -> (a -> prop) -> Property+forAllShow gen shower pf = forAllShrinkShow gen (\_ -> []) shower pf++-- | Like 'forAll', but without printing the generated value.+forAllBlind :: Testable prop+ => Gen a -> (a -> prop) -> Property+forAllBlind gen pf = forAllShrinkBlind gen (\_ -> []) pf++-- | Like 'forAll', but tries to shrink the argument for failing test cases.+forAllShrink :: (Show a, Testable prop)+ => Gen a -> (a -> [a]) -> (a -> prop) -> Property+forAllShrink gen shrinker = forAllShrinkShow gen shrinker show++-- | Like 'forAllShrink', but with an explicitly given show function.+forAllShrinkShow+ :: Testable prop+ => Gen a -> (a -> [a]) -> (a -> String) -> (a -> prop) -> Property+forAllShrinkShow gen shrinker shower pf =+ forAllShrinkBlind gen shrinker (\x -> counterexample (shower x) (pf x))++-- | Like 'forAllShrink', but without printing the generated value.+forAllShrinkBlind+ :: Testable prop+ => Gen a -> (a -> [a]) -> (a -> prop) -> Property+forAllShrinkBlind gen shrinker pf =+ MkProperty $+ gen >>= \x ->+ unProperty $+ shrinking shrinker x pf++-- | Nondeterministic choice: @p1@ '.&.' @p2@ picks randomly one of+-- @p1@ and @p2@ to test. If you test the property 100 times it+-- makes 100 random choices.+(.&.) :: (Testable prop1, Testable prop2) => prop1 -> prop2 -> Property+p1 .&. p2 =+ MkProperty $+ arbitrary >>= \b ->+ unProperty $+ counterexample (if b then "LHS" else "RHS") $+ if b then property p1 else property p2++-- | Conjunction: @p1@ '.&&.' @p2@ passes if both @p1@ and @p2@ pass.+(.&&.) :: (Testable prop1, Testable prop2) => prop1 -> prop2 -> Property+p1 .&&. p2 = conjoin [property p1, property p2]++-- | Take the conjunction of several properties.+conjoin :: Testable prop => [prop] -> Property+conjoin ps =+ MkProperty $+ do roses <- mapM (fmap unProp . unProperty . property) ps+ return (MkProp (conj id roses))+ where+ conj k [] =+ MkRose (k succeeded) []++ 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 (addLabels result . addCallbacksAndCoverage result . k) ps)+ Just False -> return rose+ Nothing -> do+ rose2@(MkRose result2 _) <- reduceRose (conj (addCallbacksAndCoverage result . k) ps)+ return $+ -- Nasty work to make sure we use the right callbacks+ case ok result2 of+ Just True -> MkRose (result2 { ok = Nothing }) []+ Just False -> rose2+ Nothing -> rose2++ addCallbacksAndCoverage result r =+ r { callbacks = callbacks result ++ callbacks r,+ requiredCoverage = requiredCoverage result ++ requiredCoverage r }+ addLabels result r =+ r { labels = labels result ++ labels r,+ classes = classes result ++ classes r,+ tables = tables result ++ tables 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]++-- | Take the disjunction of several properties.+disjoin :: Testable prop => [prop] -> Property+disjoin ps =+ MkProperty $+ do roses <- mapM (fmap unProp . unProperty . property) ps+ return (MkProp (foldr disj (MkRose failed []) roses))+ where+ disj :: Rose Result -> Rose Result -> Rose Result+ disj p q =+ do result1 <- p+ case ok result1 of+ _ | not (expect result1) -> return expectFailureError+ Just False -> do+ result2 <- q+ return $+ case ok result2 of+ _ | not (expect result2) -> expectFailureError+ Just True -> addCoverage result1 result2+ Just False ->+ MkResult {+ ok = Just False,+ expect = True,+ reason = sep (reason result1) (reason result2),+ theException = theException result1 `mplus` theException result2,+ -- The following few fields are not important because the+ -- test case has failed anyway+ abort = False,+ maybeNumTests = Nothing,+ maybeCheckCoverage = Nothing,+ maybeDiscardedRatio = Nothing,+ maybeMaxShrinks = Nothing,+ maybeMaxTestSize = Nothing,+ labels = [],+ classes = [],+ tables = [],+ requiredCoverage = [],+ callbacks =+ callbacks result1 +++ [PostFinalFailure Counterexample $ \st _res -> putLine (terminal st) ""] +++ callbacks result2,+ testCase =+ testCase result1 +++ testCase result2+ WITNESSES(= theWitnesses result1 ++ theWitnesses result2)+ }+ Nothing -> result2+ -- The "obvious" semantics of .||. has:+ -- discard .||. true = true+ -- discard .||. discard = discard+ -- but this implementation gives discard .||. true = discard.+ -- This is reasonable because evaluating result2 in the case+ -- that result1 discards is just busy-work - it won't ever+ -- cause the property to fail. On the other hand, discarding+ -- instead of returning true causes us to execute one more+ -- test case - but assuming that preconditions are cheap to+ -- evaluate, this is no more work than evaluating result2+ -- would be, while (unlike evaluating result2) it might catch+ -- a bug.+ _ -> return result1++ expectFailureError = failed { reason = "expectFailure may not occur inside a disjunction" }+ sep [] s = s+ sep s [] = s+ sep s s' = s ++ ", " ++ s'++ addCoverage result r =+ r { requiredCoverage = requiredCoverage result ++ requiredCoverage r }++-- | Like '==', but prints a counterexample when it fails.+infix 4 ===+(===) :: (Eq a, Show a) => a -> a -> Property+x === y =+ counterexample (show x ++ interpret res ++ show y) res+ where+ res = x == y+ interpret True = " == "+ interpret False = " /= "++-- | Like '/=', but prints a counterexample when it fails.+infix 4 =/=+(=/=) :: (Eq a, Show a) => a -> a -> Property+x =/= y =+ counterexample (show x ++ interpret res ++ show y) res+ where+ res = x /= y+ interpret True = " /= "+ interpret False = " == "++#ifndef NO_DEEPSEQ+-- | Checks that a value is total, i.e., doesn't crash when evaluated.+total :: NFData a => a -> Property+total x = property (rnf x)+#endif++--------------------------------------------------------------------------+-- the end.
+ src/Test/QuickCheck/Random.hs view
@@ -0,0 +1,131 @@+{-# OPTIONS_HADDOCK hide #-}+-- | A wrapper around the system random number generator. Internal QuickCheck module.+{-# LANGUAGE CPP #-}+#ifndef NO_SAFE_HASKELL+{-# LANGUAGE Trustworthy #-}+#endif+module Test.QuickCheck.Random where++import System.Random+#ifndef NO_SPLITMIX+import System.Random.SplitMix+#endif+import Data.Bits++-- | The "standard" QuickCheck random number generator.+-- A wrapper around either 'SMGen' on GHC, or 'StdGen'+-- on other Haskell systems.+#ifdef NO_SPLITMIX+newtype QCGen = QCGen StdGen+#else+newtype QCGen = QCGen SMGen+#endif++instance Show QCGen where+ showsPrec n (QCGen g) s = showsPrec n g s+instance Read QCGen where+ readsPrec n xs = [(QCGen g, ys) | (g, ys) <- readsPrec n xs]++splitImpl :: QCGen -> (QCGen, QCGen)+#ifdef NO_SPLITMIX+splitImpl (QCGen g) =+ case split g of+ (g1, g2) -> (QCGen g1, QCGen g2)+#else+splitImpl (QCGen g) =+ case splitSMGen g of+ (g1, g2) -> (QCGen g1, QCGen g2)+#endif++instance RandomGen QCGen where+#if !MIN_VERSION_random(1,3,0)+ split = splitImpl+#endif+#ifdef NO_SPLITMIX+ genRange (QCGen g) = genRange g+ next = wrapQCGen next+#else+ genRange _ = (minBound, maxBound)+ next = wrapQCGen nextInt++#ifndef OLD_RANDOM+ genWord8 = wrapQCGen genWord8+ genWord16 = wrapQCGen genWord16+ genWord32 = wrapQCGen genWord32+ genWord64 = wrapQCGen genWord64+ genWord32R r = wrapQCGen (genWord32R r)+ genWord64R r = wrapQCGen (genWord64R r)+#if MIN_VERSION_random(1,3,0)+ genShortByteString n = wrapQCGen (uniformShortByteString n)+#endif+#endif+#endif++#if MIN_VERSION_random(1,3,0)+instance SplitGen QCGen where+ splitGen = splitImpl+#endif++{-# INLINE wrapQCGen #-}+#ifdef NO_SPLITMIX+wrapQCGen :: (StdGen -> (a, StdGen)) -> (QCGen -> (a, QCGen))+#else+wrapQCGen :: (SMGen -> (a, SMGen)) -> (QCGen -> (a, QCGen))+#endif+wrapQCGen f (QCGen g) =+ case f g of+ (x, g') -> (x, QCGen g')++newQCGen :: IO QCGen+#ifdef NO_SPLITMIX+newQCGen = fmap QCGen newStdGen+#else+newQCGen = fmap QCGen newSMGen+#endif++mkQCGen :: Int -> QCGen+#ifdef NO_SPLITMIX+mkQCGen n = QCGen (mkStdGen n)+#else+mkQCGen n = QCGen (mkSMGen (fromIntegral n))+#endif++-- Parameterised in order to make this code testable.+class Splittable a where+ left, right :: a -> a++instance Splittable QCGen where+ left = fst . splitImpl+ right = snd . splitImpl++-- The logic behind 'variant'. Given a random number seed, and an integer, uses+-- splitting to transform the seed according to the integer. We use a+-- prefix-free code so that calls to integerVariant n g for different values of+-- n are guaranteed to return independent seeds.+{-# INLINE integerVariant #-}+integerVariant :: Splittable a => Integer -> a -> a+integerVariant n g+ -- Use one bit to encode the sign, then use Elias gamma coding+ -- (https://en.wikipedia.org/wiki/Elias_gamma_coding) to do the rest.+ -- Actually, the first bit encodes whether n >= 1 or not;+ -- this has the advantage that both 0 and 1 get short codes.+ | n >= 1 = gamma n $! left g+ | otherwise = gamma (1-n) $! right g+ where+ gamma n =+ encode k . zeroes k+ where+ k = ilog2 n++ encode (-1) g = g+ encode k g+ | testBit n k =+ encode (k-1) $! right g+ | otherwise =+ encode (k-1) $! left g++ zeroes 0 g = g+ zeroes k g = zeroes (k-1) $! left g++ ilog2 1 = 0+ ilog2 n = 1 + ilog2 (n `div` 2)
+ src/Test/QuickCheck/State.hs view
@@ -0,0 +1,111 @@+{-# LANGUAGE CPP #-}+#ifndef NO_SAFE_HASKELL+{-# LANGUAGE Safe #-}+#endif+{-# OPTIONS_HADDOCK hide #-}+-- | QuickCheck's internal state. Internal QuickCheck module.+module Test.QuickCheck.State where++import Test.QuickCheck.Text+import Test.QuickCheck.Random+import Data.Map(Map)++--------------------------------------------------------------------------+-- State++-- | State represents QuickCheck's internal state while testing a property.+-- The state is made visible to callback functions.+data State+ = MkState+ -- static+ { terminal :: Terminal+ -- ^ the current terminal+ , maxSuccessTests :: Int+ -- ^ maximum number of successful tests needed+ , maxDiscardedRatio :: Int+ -- ^ maximum number of discarded tests per successful test+ , coverageConfidence :: Maybe Confidence+ -- ^ how to compute the size of test cases from+ -- #tests and #discarded tests+ , numTotMaxShrinks :: !Int+ -- ^ How many shrinks to try before giving up+ , replayStartSize :: Maybe Int+ -- ^ Size to start at when replaying+ , maxTestSize :: !Int+ -- ^ Maximum size of test++ -- 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)+ -- ^ counts for each combination of labels (label/collect)+ , classes :: !(Map String Int)+ -- ^ counts for each class of test case (classify/cover)+ , tables :: !(Map String (Map String Int))+ -- ^ tables collected using tabulate+ , requiredCoverage :: !(Map (Maybe String, String) Double)+ -- ^ coverage requirements+ , expected :: !Bool+ -- ^ indicates the expected result of the property+ , 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+ }++-- | The statistical parameters used by 'Test.QuickCheck.checkCoverage'.+data Confidence =+ Confidence {+ certainty :: Integer,+ -- ^ How certain 'Test.QuickCheck.checkCoverage' must be before the property+ -- fails. If the coverage requirement is met, and the certainty parameter is+ -- @n@, then you should get a false positive at most one in @n@ runs of+ -- QuickCheck. The default value is @10^9@.+ --+ -- Lower values will speed up 'Test.QuickCheck.checkCoverage' at the cost of+ -- false positives.+ --+ -- If you are using 'Test.QuickCheck.checkCoverage' as part of a test suite,+ -- you should be careful not to set @certainty@ too low. If you want, say, a+ -- 1% chance of a false positive during a project's lifetime, then+ -- certainty@ should be set to at least @100 * m * n@, where @m@ is the+ -- number of uses of 'Test.QuickCheck.cover' in the test suite, and @n@ is+ -- the number of times you expect the test suite to be run during the+ -- project's lifetime. The default value is chosen to be big enough for most+ -- projects.+ tolerance :: Double+ -- ^ For statistical reasons, 'Test.QuickCheck.checkCoverage' will not+ -- reject coverage levels that are only slightly below the required levels.+ -- If the required level is @p@ then an actual level of @tolerance * p@+ -- will be accepted. The default value is @0.9@.+ --+ -- Lower values will speed up 'Test.QuickCheck.checkCoverage' at the cost of+ -- not detecting minor coverage violations.+ }+ deriving Show++-- | TestProgress, contains information that might be interesting to external+-- libraries, e.g. Tasty. From this it is possible to install your own callbacks+-- that reports e.g. progress.+data TestProgress+ = TestProgress+ { currentPassed :: Int -- ^ Number of tests passed so far+ , currentDiscarded :: Int -- ^ Number of discared tests so far+ , maxTests :: Int -- ^ Number of tests to execute+ , currentShrinks :: Int -- ^ Number of successful shrinking steps+ , currentFailedShrinks :: Int -- ^ Number of failed shrinking steps since last successful one+ , currentTotalShrinks :: Int -- ^ Total number of failed shrinking steps+ } deriving Show++--------------------------------------------------------------------------+-- the end.
+ src/Test/QuickCheck/Test.hs view
@@ -0,0 +1,780 @@+{-# OPTIONS_HADDOCK hide #-}+-- | The main test loop.+{-# LANGUAGE CPP #-}+#ifndef NO_TYPEABLE+{-# LANGUAGE DeriveDataTypeable #-}+#endif+#ifndef NO_SAFE_HASKELL+{-# LANGUAGE Trustworthy #-}+#endif+module Test.QuickCheck.Test where++--------------------------------------------------------------------------+-- imports++import Control.Applicative+import Test.QuickCheck.Gen+import Test.QuickCheck.Property hiding ( Result( reason, theException, labels, classes, tables ), (.&.) )+import qualified Test.QuickCheck.Property as P+import Test.QuickCheck.Text+import Test.QuickCheck.State hiding (labels, classes, tables, requiredCoverage)+import qualified Test.QuickCheck.State as S+import Test.QuickCheck.Exception+import Test.QuickCheck.Random+#if defined(MIN_VERSION_containers)+#if MIN_VERSION_containers(0,5,0)+import qualified Data.Map.Strict as Map+#else+import qualified Data.Map as Map+#endif+#else+import qualified Data.Map as Map+#endif+import qualified Data.Set as Set+import Data.Set(Set)+import Data.Map(Map)++import Data.Char+ ( isSpace+ )++import Data.List+ ( sort+ , sortBy+ , group+ , intersperse+ )++import Data.Maybe(fromMaybe, isNothing, catMaybes)+import Data.Ord(comparing)+import Text.Printf(printf)+import Control.Monad+import Data.Bits+import Data.Maybe++#ifndef NO_TYPEABLE+import Data.Typeable (Typeable)+#endif++--------------------------------------------------------------------------+-- quickCheck++-- * Running tests++-- | Args specifies arguments to the QuickCheck driver+data Args+ = Args+ { replay :: Maybe (QCGen,Int)+ -- ^ Should we replay a previous test?+ -- Note: saving a seed from one version of QuickCheck and+ -- replaying it in another is not supported.+ -- If you want to store a test case permanently you should save+ -- the test case itself.+ , maxSuccess :: Int+ -- ^ Maximum number of successful tests before succeeding. Testing stops+ -- at the first failure. If all tests are passing and you want to run more tests,+ -- increase this number.+ , maxDiscardRatio :: Int+ -- ^ Maximum number of discarded tests per successful test before giving up+ , maxSize :: Int+ -- ^ Size to use for the biggest test cases+ , chatty :: Bool+ -- ^ Whether to print anything+ , maxShrinks :: Int+ -- ^ Maximum number of shrinks to do before giving up. Setting this to zero+ -- turns shrinking off.+ }+ deriving ( Show, Read+#ifndef NO_TYPEABLE+ , Typeable+#endif+ )++-- | Result represents the test result+data Result+ -- | A successful test run+ = Success+ { numTests :: Int+ -- ^ Number of tests performed+ , numDiscarded :: Int+ -- ^ Number of tests skipped+ , labels :: !(Map [String] Int)+ -- ^ The number of test cases having each combination of labels (see 'label')+ , classes :: !(Map String Int)+ -- ^ The number of test cases having each class (see 'classify')+ , tables :: !(Map String (Map String Int))+ -- ^ Data collected by 'tabulate'+ , output :: String+ -- ^ Printed output+ }+ -- | Given up+ | GaveUp+ { numTests :: Int+ , numDiscarded :: Int+ -- ^ Number of tests skipped+ , labels :: !(Map [String] Int)+ , classes :: !(Map String Int)+ , tables :: !(Map String (Map String Int))+ , output :: String+ }+ -- | A failed test run+ | Failure+ { numTests :: Int+ , numDiscarded :: Int+ -- ^ Number of tests skipped+ , numShrinks :: Int+ -- ^ Number of successful shrinking steps performed+ , numShrinkTries :: Int+ -- ^ Number of unsuccessful shrinking steps performed+ , numShrinkFinal :: Int+ -- ^ Number of unsuccessful shrinking steps performed since last successful shrink+ , usedSeed :: QCGen+ -- ^ What seed was used+ , usedSize :: Int+ -- ^ What was the test size+ , reason :: String+ -- ^ Why did the property fail+ , theException :: Maybe AnException+ -- ^ The exception the property threw, if any+ , output :: String+ , failingTestCase :: [String]+ -- ^ The test case which provoked the failure+ , failingLabels :: [String]+ -- ^ The test case's labels (see 'label')+ , failingClasses :: Set String+ -- ^ The test case's classes (see 'classify')+#ifndef NO_TYPEABLE+ , witnesses :: [Witness]+ -- ^ The existentially quantified witnesses provided by 'witness'+#endif+ }+ -- | A property that should have failed did not+ | NoExpectedFailure+ { numTests :: Int+ , numDiscarded :: Int+ -- ^ Number of tests skipped+ , labels :: !(Map [String] Int)+ , classes :: !(Map String Int)+ , tables :: !(Map String (Map String Int))+ , output :: String+ }+ deriving ( Show )++-- | Check if the test run result was a success+isSuccess :: Result -> Bool+isSuccess Success{} = True+isSuccess _ = False++-- | The default test arguments+stdArgs :: Args+stdArgs = Args+ { replay = Nothing+ , maxSuccess = 100+ , maxDiscardRatio = 10+ , maxSize = 100+ , chatty = True+ , maxShrinks = maxBound+ }++-- | Tests a property and prints the results to 'System.IO.stdout'.+--+-- By default up to 100 tests are performed, which may not be enough+-- to find all bugs. To run more tests, use 'withNumTests'.+--+-- If you want to get the counterexample as a Haskell value,+-- rather than just printing it, try the+-- <http://hackage.haskell.org/package/quickcheck-with-counterexamples quickcheck-with-counterexamples>+-- package.++quickCheck :: Testable prop => prop -> IO ()+quickCheck p = quickCheckWith stdArgs p++-- | Tests a property, using test arguments, and prints the results to 'System.IO.stdout'.+quickCheckWith :: Testable prop => Args -> prop -> IO ()+quickCheckWith args p = quickCheckWithResult args p >> return ()++-- | Tests a property, produces a test result, and prints the results to 'System.IO.stdout'.+quickCheckResult :: Testable prop => prop -> IO Result+quickCheckResult p = quickCheckWithResult stdArgs p++-- | Tests a property, using test arguments, produces a test result, and prints the results to 'System.IO.stdout'.+quickCheckWithResult :: Testable prop => Args -> prop -> IO Result+quickCheckWithResult a p =+ withState a (\s -> test s (property p))++-- | Re-run a property with the seed and size that failed in a run of 'quickCheckResult'.+recheck :: Testable prop => Result -> prop -> IO ()+recheck res@Failure{} = quickCheckWith stdArgs{ replay = Just (usedSeed res, usedSize res)} . once+recheck _ = error "Can only recheck tests that failed with a counterexample."++withState :: Args -> (State -> IO a) -> IO a+withState a test = (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+ , coverageConfidence = Nothing+ , maxDiscardedRatio = maxDiscardRatio a+ , replayStartSize = snd <$> replay a+ , maxTestSize = maxSize a+ , numTotMaxShrinks = maxShrinks a+ , numSuccessTests = 0+ , numDiscardedTests = 0+ , numRecentlyDiscardedTests = 0+ , S.labels = Map.empty+ , S.classes = Map.empty+ , S.tables = Map.empty+ , S.requiredCoverage = Map.empty+ , expected = True+ , randomSeed = rnd+ , numSuccessShrinks = 0+ , numTryShrinks = 0+ , numTotTryShrinks = 0+ }++computeSize :: State -> Int+computeSize MkState{replayStartSize = Just s,numSuccessTests = 0,numRecentlyDiscardedTests=0} = s+-- NOTE: Beware that changing this means you also have to change `prop_discardCoverage` as that currently relies+-- on the sequence produced by this function.+computeSize MkState{maxSuccessTests = ms, maxTestSize = mts, maxDiscardedRatio = md,numSuccessTests=n,numRecentlyDiscardedTests=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` mts + mts <= ms ||+ n >= ms ||+ ms `mod` mts == 0 = (n `mod` mts + d `div` dDenom) `min` mts+ | otherwise =+ ((n `mod` mts) * mts `div` (ms `mod` mts) + d `div` dDenom) `min` mts+ where+ -- The inverse of the rate at which we increase size as a function of discarded tests+ -- if the discard ratio is high we can afford this to be slow, but if the discard ratio+ -- is low we risk bowing out too early+ dDenom+ | md > 0 = (ms * md `div` 3) `clamp` (1, 10)+ | otherwise = 1 -- Doesn't matter because there will be no discards allowed+ n `roundTo` m = (n `div` m) * m++clamp :: Ord a => a -> (a, a) -> a+clamp x (l, h) = max l (min x h)++-- | Tests a property and prints the results and all test cases generated to 'System.IO.stdout'.+-- This is just a convenience function that means the same as @'quickCheck' . 'verbose'@.+--+-- Note: for technical reasons, the test case is printed out /after/+-- the property is tested. To debug a property that goes into an+-- infinite loop, use 'within' to add a timeout instead.+verboseCheck :: Testable prop => prop -> IO ()+verboseCheck p = quickCheck (verbose p)++-- | Tests a property, using test arguments, and prints the results and all test cases generated to 'System.IO.stdout'.+-- This is just a convenience function that combines 'quickCheckWith' and 'verbose'.+--+-- Note: for technical reasons, the test case is printed out /after/+-- the property is tested. To debug a property that goes into an+-- infinite loop, use 'within' to add a timeout instead.+verboseCheckWith :: Testable prop => Args -> prop -> IO ()+verboseCheckWith args p = quickCheckWith args (verbose p)++-- | Tests a property, produces a test result, and prints the results and all test cases generated to 'System.IO.stdout'.+-- This is just a convenience function that combines 'quickCheckResult' and 'verbose'.+--+-- Note: for technical reasons, the test case is printed out /after/+-- the property is tested. To debug a property that goes into an+-- infinite loop, use 'within' to add a timeout instead.+verboseCheckResult :: Testable prop => prop -> IO Result+verboseCheckResult p = quickCheckResult (verbose p)++-- | Tests a property, using test arguments, produces a test result, and prints the results and all test cases generated to 'System.IO.stdout'.+-- This is just a convenience function that combines 'quickCheckWithResult' and 'verbose'.+--+-- Note: for technical reasons, the test case is printed out /after/+-- the property is tested. To debug a property that goes into an+-- infinite loop, use 'within' to add a timeout instead.+verboseCheckWithResult :: Testable prop => Args -> prop -> IO Result+verboseCheckWithResult a p = quickCheckWithResult a (verbose p)++--------------------------------------------------------------------------+-- main test loop++test :: State -> Property -> IO Result+test st prop+ | finishedSuccessfully st = doneTesting st+ | finishedInsufficientCoverage st = failCoverage st+ | tooManyDiscards st = giveUp st+ | otherwise = runATest st prop++finishedSuccessfully :: State -> Bool+finishedSuccessfully st+ | checkingCoverage st =+ and [ timeToCheckCoverage st+ , coverageKnownSufficient st+ , numSuccessTests st >= maxSuccessTests st+ ]+ | otherwise = numSuccessTests st >= maxSuccessTests st++finishedInsufficientCoverage :: State -> Bool+finishedInsufficientCoverage st =+ and [ checkingCoverage st+ , timeToCheckCoverage st+ , coverageKnownInsufficient st+ ]++tooManyDiscards :: State -> Bool+tooManyDiscards st+ | maxDiscardedRatio st > 0 = numDiscardedTests st `div` maxDiscardedRatio st >= max (numSuccessTests st) (maxSuccessTests st)+ | otherwise = numDiscardedTests st > 0++checkingCoverage :: State -> Bool+checkingCoverage st = isJust (coverageConfidence st)++timeToCheckCoverage :: State -> Bool+timeToCheckCoverage st+ -- This is the time when we would normally finish testing, so lets check+ -- it now to see if we can finish testing already+ | numSuccessTests st == maxSuccessTests st && numRecentlyDiscardedTests st == 0 = True+ -- We are on test 100 * 2^k for k > 0+ | otherwise =+ and [ numSuccessTests st > 0+ , numSuccessTests st `mod` 100 == 0+ , powerOfTwo (numSuccessTests st `div` 100)+ ]+ where powerOfTwo n = n .&. (n - 1) == 0++coverageKnownSufficient :: State -> Bool+coverageKnownSufficient st@MkState{coverageConfidence=Just confidence} =+ and [ sufficientlyCovered confidence tot n p | (_, _, tot, n, p) <- allCoverage st ]+coverageKnownSufficient _ = True++coverageKnownInsufficient :: State -> Bool+coverageKnownInsufficient st@MkState{coverageConfidence=Just confidence} =+ or [ insufficientlyCovered (Just (certainty confidence)) tot n p+ | (_, _, tot, n, p) <- allCoverage st ]+coverageKnownInsufficient _ = False++failCoverage :: State -> IO Result+failCoverage st =+ -- The last test wasn't actually successful, as the coverage failed+ -- also this prevents an off-by-one error in the printing+ runATest st{numSuccessTests = numSuccessTests st - 1}+ $ foldr counterexample (property failed{P.reason = "Insufficient coverage"})+ (paragraphs [theLabels, theTables])+ where (theLabels, theTables) = labelsAndTables st++doneTesting :: State -> IO Result+doneTesting st+ | expected st == False = do+ putPart (terminal st)+ ( bold ("*** Failed!")+ ++ " Passed "+ ++ showTestCount st+ ++ " (expected failure)"+ )+ finished NoExpectedFailure+ | otherwise = do+ putPart (terminal st)+ ( "+++ OK, passed "+ ++ showTestCount st+ )+ finished Success+ where+ finished k = do+ success st+ theOutput <- terminalOutput (terminal st)+ return (k (numSuccessTests st) (numDiscardedTests st) (S.labels st) (S.classes st) (S.tables st) theOutput)++giveUp :: State -> IO Result+giveUp st =+ do -- CALLBACK gave_up?+ putPart (terminal st)+ ( bold ("*** Gave up!")+ ++ " Passed only "+ ++ showTestCount st+ ++ " tests"+ )+ success st+ theOutput <- terminalOutput (terminal st)+ return GaveUp{ numTests = numSuccessTests st+ , numDiscarded = numDiscardedTests st+ , labels = S.labels st+ , classes = S.classes st+ , tables = S.tables st+ , output = theOutput+ }++showTestCount :: State -> String+showTestCount st = formatTestCount (numSuccessTests st) (numDiscardedTests st)++formatTestCount :: Int -> Int -> String+formatTestCount succeeded discarded =+ number succeeded "test"+ ++ concat [ "; " ++ show discarded ++ " discarded"+ | discarded > 0+ ]++runATest :: State -> Property -> IO Result+runATest st prop =+ do -- CALLBACK before_test+ putTemp (terminal st)+ ( "("+ ++ showTestCount st+ ++ ")"+ )++ let size = computeSize st++ MkRose res ts <- protectRose (reduceRose (unProp (unGen (unProperty prop) rnd1 size)))+ res <- callbackPostTest st res++ let continue :: (State -> IO Result) -> State -> IO Result+ continue break st'+ | abort res = break $ updateState st'+ | otherwise = test (updateState st') prop++ updateState st0 = addNewOptions $ st0{ randomSeed = rnd2 }++ addNewOptions st0 = st0{ maxSuccessTests = fromMaybe (maxSuccessTests st0) (maybeNumTests res)+ , maxDiscardedRatio = fromMaybe (maxDiscardedRatio st0) (maybeDiscardedRatio res)+ , numTotMaxShrinks = fromMaybe (numTotMaxShrinks st0) (maybeMaxShrinks res)+ , maxTestSize = fromMaybe (maxTestSize st0) (maybeMaxTestSize res)+ , expected = expect res+ }++ addCoverageInfo st0 =+ st0{ coverageConfidence = maybeCheckCoverage res `mplus` coverageConfidence st0+ , S.labels = Map.insertWith (+) (P.labels res) 1 (S.labels st0)+ , S.classes = Map.unionWith (+) (S.classes st0)+ (Map.fromList [ (s, if b then 1 else 0) | (s, b) <- P.classes res ])+ , S.tables =+ foldr (\(tab, x) -> Map.insertWith (Map.unionWith (+)) tab (Map.singleton x 1))+ (S.tables st0) (P.tables res)+ , S.requiredCoverage =+ foldr (\(key, value, p) -> Map.insertWith max (key, value) p)+ (S.requiredCoverage st0) (P.requiredCoverage res)+ }++ stC = addCoverageInfo st++ case res of+ MkResult{ok = Just True} -> -- successful test+ do continue doneTesting+ stC{ numSuccessTests = numSuccessTests st + 1+ , numRecentlyDiscardedTests = 0+ }++ MkResult{ok = Nothing} -> -- discarded test+ do continue giveUp+ -- Don't add coverage info from this test+ st{ numDiscardedTests = numDiscardedTests st + 1+ , numRecentlyDiscardedTests = numRecentlyDiscardedTests st + 1+ }++ MkResult{ok = Just False} -> -- failed test+ do (numShrinks, totFailed, lastFailed, res) <- foundFailure (addNewOptions stC) res ts+ theOutput <- terminalOutput (terminal stC)+ if not (expect res) then+ return Success{ labels = S.labels stC,+ classes = S.classes stC,+ tables = S.tables stC,+ numTests = numSuccessTests stC+1,+ numDiscarded = numDiscardedTests stC,+ output = theOutput }+ else do+ testCase <- mapM showCounterexample (P.testCase res)+ return Failure{ usedSeed = randomSeed stC -- correct! (this will be split first)+ , usedSize = size+ , numTests = numSuccessTests stC + 1+ , numDiscarded = numDiscardedTests stC+ , numShrinks = numShrinks+ , numShrinkTries = totFailed+ , numShrinkFinal = lastFailed+ , output = theOutput+ , reason = P.reason res+ , theException = P.theException res+ , failingTestCase = testCase+ , failingLabels = P.labels res+ , failingClasses = Set.fromList (map fst $ filter snd $ P.classes res)+#ifndef NO_TYPEABLE+ , witnesses = theWitnesses res+#endif+ }+ where+ (rnd1,rnd2) = splitImpl (randomSeed st)++failureSummary :: State -> P.Result -> String+failureSummary st res = fst (failureSummaryAndReason st res)++failureReason :: State -> P.Result -> [String]+failureReason st res = snd (failureSummaryAndReason st res)++failureSummaryAndReason :: State -> P.Result -> (String, [String])+failureSummaryAndReason st res = (summary, full)+ where+ summary =+ header +++ short 26 (oneLine theReason ++ " ") +++ count True ++ "..."++ full =+ (header +++ (if isOneLine theReason then theReason ++ " " else "") +++ count False ++ ":"):+ if isOneLine theReason then [] else lines theReason++ theReason = P.reason res++ header =+ if expect res then+ bold "*** Failed! "+ else "+++ OK, failed as expected. "++ count full =+ "(after " ++ number (numSuccessTests st+1) "test" +++ concat [+ " and " +++ show (numSuccessShrinks st) +++ concat [ "." ++ show (numTryShrinks st) | showNumTryShrinks ] +++ " shrink" +++ (if numSuccessShrinks st == 1 && not showNumTryShrinks then "" else "s")+ | numSuccessShrinks st > 0 || showNumTryShrinks ] +++ ")"+ where+ showNumTryShrinks = full && numTryShrinks st > 0++success :: State -> IO ()+success st = do+ mapM_ (putLine $ terminal st) (paragraphs [short, long])+ where+ (short, long) =+ case labelsAndTables st of+ ([msg], long) ->+ ([" (" ++ dropWhile isSpace msg ++ ")."], long)+ ([], long) ->+ (["."], long)+ (short, long) ->+ (":":short, long)++labelsAndTables :: State -> ([String], [String])+labelsAndTables st = (theLabels, theTables)+ where+ theLabels :: [String]+ theLabels =+ paragraphs $+ [ showTable (numSuccessTests st) Nothing m+ | m <- S.classes st:Map.elems numberedLabels ]++ numberedLabels :: Map Int (Map String Int)+ numberedLabels =+ Map.fromListWith (Map.unionWith (+)) $+ [ (i, Map.singleton l n)+ | (labels, n) <- Map.toList (S.labels st),+ (i, l) <- zip [0..] labels ]++ theTables :: [String]+ theTables =+ paragraphs $+ [ showTable (sum (Map.elems m)) (Just table) m+ | (table, m) <- Map.toList (S.tables st) ] +++ [[ (case mtable of Nothing -> "Only "; Just table -> "Table '" ++ table ++ "' had only ")+ ++ lpercent n tot ++ " " ++ label ++ ", but expected " ++ lpercentage p tot+ | (mtable, label, tot, n, p) <- allCoverage st,+ insufficientlyCovered (fmap certainty (coverageConfidence st)) tot n p ]]++showTable :: Int -> Maybe String -> Map String Int -> [String]+showTable k mtable m =+ [table ++ " " ++ total ++ ":" | Just table <- [mtable]] +++ (map format .+ -- Descending order of occurrences+ reverse . sortBy (comparing snd) .+ -- If #occurences the same, sort in increasing order of key+ -- (note: works because sortBy is stable)+ reverse . sortBy (comparing fst) $ Map.toList m)+ where+ format (key, v) =+ rpercent v k ++ " " ++ key++ total = printf "(%d in total)" k++--------------------------------------------------------------------------+-- main shrinking loop++foundFailure :: State -> P.Result -> [Rose P.Result] -> IO (Int, Int, Int, P.Result)+foundFailure st res ts =+ do localMin st{ numTryShrinks = 0 } res ts++localMin :: State -> P.Result -> [Rose P.Result] -> IO (Int, Int, Int, P.Result)+-- Don't try to shrink for too long+localMin st res ts+ | numSuccessShrinks st + numTotTryShrinks st >= numTotMaxShrinks st =+ localMinFound st res+localMin st res ts = do+ r <- tryEvaluateIO $+ putTemp (terminal st) (failureSummary st res)+ case r of+ Left err ->+ localMinFound st (exception "Exception while printing status message" err) { callbacks = callbacks res }+ Right () -> do+ r <- tryEvaluate ts+ case r of+ Left err+ | isDiscard err -> localMinFound st res+ | otherwise ->+ 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, P.Result)+localMin' st res [] = localMinFound st res+localMin' st res (t:ts) =+ do -- CALLBACK before_test+ MkRose res' ts' <- protectRose (reduceRose t)+ res' <- callbackPostTest st res'+ if ok res' == Just False+ then localMin st{ numSuccessShrinks = numSuccessShrinks st + 1,+ numTryShrinks = 0 } res' ts'+ else localMin st{ numTryShrinks = numTryShrinks st + 1,+ numTotTryShrinks = numTotTryShrinks st + 1 } res ts++localMinFound :: State -> P.Result -> IO (Int, Int, Int, P.Result)+localMinFound st res =+ do sequence_ [ putLine (terminal st) msg | msg <- failureReason st 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, res)++--------------------------------------------------------------------------+-- callbacks++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 = 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 ()++----------------------------------------------------------------------+-- computing coverage++sufficientlyCovered :: Confidence -> Int -> Int -> Double -> Bool+sufficientlyCovered confidence n k p =+ -- Accept the coverage if, with high confidence, the actual probability is+ -- at least 0.9 times the required one.+ wilsonLow (fromIntegral k) (fromIntegral n) (1 / fromIntegral err) >= tol * p+ where+ err = certainty confidence+ tol = tolerance confidence++insufficientlyCovered :: Maybe Integer -> Int -> Int -> Double -> Bool+insufficientlyCovered Nothing n k p =+ fromIntegral k < p * fromIntegral n+insufficientlyCovered (Just err) n k p =+ wilsonHigh (fromIntegral k) (fromIntegral n) (1 / fromIntegral err) < p++-- https://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval#Wilson_score_interval+-- Note:+-- https://www.ncss.com/wp-content/themes/ncss/pdf/Procedures/PASS/Confidence_Intervals_for_One_Proportion.pdf+-- suggests we should use a instead of a/2 for a one-sided test. Look+-- into this.+wilson :: Integer -> Integer -> Double -> Double+wilson k n z =+ (p + z*z/(2*nf) + z*sqrt (p*(1-p)/nf + z*z/(4*nf*nf)))/(1 + z*z/nf)+ where+ nf = fromIntegral n+ p = fromIntegral k / fromIntegral n++wilsonLow :: Integer -> Integer -> Double -> Double+wilsonLow k n a = wilson k n (invnormcdf (a/2))++wilsonHigh :: Integer -> Integer -> Double -> Double+wilsonHigh k n a = wilson k n (invnormcdf (1-a/2))++-- Algorithm taken from+-- https://web.archive.org/web/20151110174102/http://home.online.no/~pjacklam/notes/invnorm/+-- Accurate to about one part in 10^9.+--+-- The 'erf' package uses the same algorithm, but with an extra step+-- to get a fully accurate result, which we skip because it requires+-- the 'erfc' function.+invnormcdf :: Double -> Double+invnormcdf p+ | p < 0 = 0/0+ | p > 1 = 0/0+ | p == 0 = -1/0+ | p == 1 = 1/0+ | p < p_low =+ let+ q = sqrt(-2*log(p))+ in+ (((((c1*q+c2)*q+c3)*q+c4)*q+c5)*q+c6) /+ ((((d1*q+d2)*q+d3)*q+d4)*q+1)+ | p <= p_high =+ let+ q = p - 0.5+ r = q*q+ in+ (((((a1*r+a2)*r+a3)*r+a4)*r+a5)*r+a6)*q /+ (((((b1*r+b2)*r+b3)*r+b4)*r+b5)*r+1)+ | otherwise =+ let+ q = sqrt(-2*log(1-p))+ in+ -(((((c1*q+c2)*q+c3)*q+c4)*q+c5)*q+c6) /+ ((((d1*q+d2)*q+d3)*q+d4)*q+1)+ where+ a1 = -3.969683028665376e+01+ a2 = 2.209460984245205e+02+ a3 = -2.759285104469687e+02+ a4 = 1.383577518672690e+02+ a5 = -3.066479806614716e+01+ a6 = 2.506628277459239e+00++ b1 = -5.447609879822406e+01+ b2 = 1.615858368580409e+02+ b3 = -1.556989798598866e+02+ b4 = 6.680131188771972e+01+ b5 = -1.328068155288572e+01++ c1 = -7.784894002430293e-03+ c2 = -3.223964580411365e-01+ c3 = -2.400758277161838e+00+ c4 = -2.549732539343734e+00+ c5 = 4.374664141464968e+00+ c6 = 2.938163982698783e+00++ d1 = 7.784695709041462e-03+ d2 = 3.224671290700398e-01+ d3 = 2.445134137142996e+00+ d4 = 3.754408661907416e+00++ p_low = 0.02425+ p_high = 1 - p_low+++allCoverage :: State -> [(Maybe String, String, Int, Int, Double)]+allCoverage st =+ [ (key, value, tot, n, p)+ | ((key, value), p) <- Map.toList (S.requiredCoverage st),+ let tot =+ case key of+ Just key -> Map.findWithDefault 0 key totals+ Nothing -> numSuccessTests st,+ let n = Map.findWithDefault 0 value (Map.findWithDefault Map.empty key combinedCounts) ]+ where+ combinedCounts :: Map (Maybe String) (Map String Int)+ combinedCounts =+ Map.insert Nothing (S.classes st)+ (Map.mapKeys Just (S.tables st))++ totals :: Map String Int+ totals = fmap (sum . Map.elems) (S.tables st)++--------------------------------------------------------------------------+-- the end.
+ src/Test/QuickCheck/Text.hs view
@@ -0,0 +1,236 @@+{-# LANGUAGE CPP #-}+#ifndef NO_SAFE_HASKELL+{-# LANGUAGE Safe #-}+#endif+{-# OPTIONS_HADDOCK hide #-}+-- | Terminal control and text helper functions. Internal QuickCheck module.+module Test.QuickCheck.Text+ ( Str(..)+ , ranges++ , number+ , short+ , showErr+ , oneLine+ , isOneLine+ , bold+ , ljust, rjust, centre, lpercent, rpercent, lpercentage, rpercentage+ , drawTable, Cell(..)+ , paragraphs++ , newTerminal+ , withStdioTerminal+ , withHandleTerminal+ , withNullTerminal+ , terminalOutput+ , handle+ , Terminal+ , putTemp+ , putPart+ , putLine+ )+ where++--------------------------------------------------------------------------+-- imports++import System.IO+ ( hFlush+ , hPutStr+ , stdout+ , stderr+ , Handle+ , BufferMode (..)+ , hGetBuffering+ , hSetBuffering+ , hIsTerminalDevice+ )++import Data.IORef+import Data.List (intersperse, transpose)+import Text.Printf+import Test.QuickCheck.Exception++--------------------------------------------------------------------------+-- literal string++newtype Str = MkStr String++instance Show Str where+ show (MkStr s) = s++ranges :: (Show a, Integral a) => a -> a -> Str+ranges k n = MkStr (show n' ++ " -- " ++ show (n'+k-1))+ where+ n' = k * (n `div` k)++--------------------------------------------------------------------------+-- formatting++number :: Int -> String -> String+number n s = show n ++ " " ++ s ++ if n == 1 then "" else "s"++short :: Int -> String -> String+short n s+ | n < k = take (n-2-i) s ++ ".." ++ drop (k-i) s+ | otherwise = s+ where+ k = length s+ i = if n >= 5 then 3 else 0++showErr :: Show a => a -> String+showErr = unwords . words . show++oneLine :: String -> String+oneLine = unwords . words++isOneLine :: String -> Bool+isOneLine xs = '\n' `notElem` xs++ljust n xs = xs ++ replicate (n - length xs) ' '+rjust n xs = replicate (n - length xs) ' ' ++ xs+centre n xs =+ ljust n $+ replicate ((n - length xs) `div` 2) ' ' ++ xs++lpercent, rpercent :: (Integral a, Integral b) => a -> b -> String+lpercent n k =+ lpercentage (fromIntegral n / fromIntegral k) k++rpercent n k =+ rpercentage (fromIntegral n / fromIntegral k) k++lpercentage, rpercentage :: Integral a => Double -> a -> String+lpercentage p n =+ printf "%.*f" places (100*p) ++ "%"+ where+ -- Show no decimal places if k <= 100,+ -- one decimal place if k <= 1000,+ -- two decimal places if k <= 10000, and so on.+ places :: Integer+ places =+ ceiling (logBase 10 (fromIntegral n) - 2 :: Double) `max` 0++rpercentage p n = padding ++ lpercentage p n+ where+ padding = if p < 0.1 then " " else ""++data Cell = LJust String | RJust String | Centred String deriving Show++text :: Cell -> String+text (LJust xs) = xs+text (RJust xs) = xs+text (Centred xs) = xs++-- Flatten a table into a list of rows+flattenRows :: [[Cell]] -> [String]+flattenRows rows = map row rows+ where+ cols = transpose rows+ widths = map (maximum . map (length . text)) cols++ row cells = concat (intersperse " " (zipWith cell widths cells))+ cell n (LJust xs) = ljust n xs+ cell n (RJust xs) = rjust n xs+ cell n (Centred xs) = centre n xs++-- Draw a table given a header and contents+drawTable :: [String] -> [[Cell]] -> [String]+drawTable headers table =+ [line] +++ [border '|' ' ' header | header <- headers] +++ [line | not (null headers) && not (null rows)] +++ [border '|' ' ' row | row <- rows] +++ [line]+ where+ rows = flattenRows table++ headerwidth = maximum (0:map length headers)+ bodywidth = maximum (0:map length rows)+ width = max headerwidth bodywidth++ line = border '+' '-' $ replicate width '-'+ border x y xs = [x, y] ++ centre width xs ++ [y, x]++paragraphs :: [[String]] -> [String]+paragraphs = concat . intersperse [""] . filter (not . null)++bold :: String -> String+-- not portable:+--bold s = "\ESC[1m" ++ s ++ "\ESC[0m"+bold s = s -- for now++--------------------------------------------------------------------------+-- putting strings++data Terminal+ = MkTerminal (IORef ShowS) (IORef Int) (String -> IO ()) (String -> IO ())++newTerminal :: (String -> IO ()) -> (String -> IO ()) -> IO Terminal+newTerminal out err =+ do res <- newIORef (showString "")+ tmp <- newIORef 0+ return (MkTerminal res tmp out err)++withBuffering :: IO a -> IO a+withBuffering action = do+ mode <- hGetBuffering stderr+ -- By default stderr is unbuffered. This is very slow, hence we explicitly+ -- enable line buffering.+ hSetBuffering stderr LineBuffering+ action `finally` hSetBuffering stderr mode++withHandleTerminal :: Handle -> Maybe Handle -> (Terminal -> IO a) -> IO a+withHandleTerminal outh merrh action = do+ let+ err =+ case merrh of+ Nothing -> const (return ())+ Just errh -> handle errh+ newTerminal (handle outh) err >>= action++withStdioTerminal :: (Terminal -> IO a) -> IO a+withStdioTerminal action = do+ isatty <- hIsTerminalDevice stderr+ if isatty then+ withBuffering (withHandleTerminal stdout (Just stderr) action)+ else+ withBuffering (withHandleTerminal stdout Nothing action)++withNullTerminal :: (Terminal -> IO a) -> IO a+withNullTerminal action =+ newTerminal (const (return ())) (const (return ())) >>= action++terminalOutput :: Terminal -> IO String+terminalOutput (MkTerminal res _ _ _) = fmap ($ "") (readIORef res)++handle :: Handle -> String -> IO ()+handle h s = do+ hPutStr h s+ hFlush h++putPart, putTemp, putLine :: Terminal -> String -> IO ()+putPart tm@(MkTerminal res _ out _) s =+ do putTemp tm ""+ force s+ out s+ modifyIORef res (. showString s)+ where+ force :: [a] -> IO ()+ force = evaluate . seqList++ seqList :: [a] -> ()+ seqList [] = ()+ seqList (x:xs) = x `seq` seqList xs++putLine tm s = putPart tm (s ++ "\n")++putTemp tm@(MkTerminal _ tmp _ err) s =+ do oldLen <- readIORef tmp+ let newLen = length s+ maxLen = max newLen oldLen+ err $ s ++ replicate (maxLen - newLen) ' ' ++ replicate maxLen '\b'+ writeIORef tmp newLen++--------------------------------------------------------------------------+-- the end.
+ test-hugs view
@@ -0,0 +1,29 @@+#!/bin/sh++set -e++CABAL=${CABAL:-cabal}+HC=${HC:-ghc}++# Install cpphs if it is not in path+command -v cpphs || ${CABAL} v2-install --ignore-project --with-compiler "$HC" cpphs++# Regenerate quickcheck-hugs+sh make-hugs+find quickcheck-hugs++die() {+ echo "TEST FAILED"+ exit 1+}++dotest() {+ echo "$2" | hugs -98 -Pquickcheck-hugs: -p'> ' "$1" | tee hugs.output+ grep -q "$3" hugs.output || die+}++# Simple tests+dotest Test.QuickCheck 'quickCheck $ \xs -> reverse (reverse xs) === (xs :: [Int])' "OK, passed 100 tests."+dotest Test.QuickCheck 'quickCheck $ \xs -> reverse xs === (xs :: [Int])' "\[0,1\]"+echo+echo 'All tests passed!'
+ test-mhs view
@@ -0,0 +1,25 @@+#!/bin/sh++set -e++mcabal -r install++die() {+ echo "TEST FAILED"+ exit 1+}++dotest() {+ cat >Main.hs <<EOF+import $1+main = $2+EOF+ mhs Main.hs -oMain && ./Main | tee mhs.output+ grep -q "$3" mhs.output || die+}++# Simple tests+dotest Test.QuickCheck 'quickCheck $ \xs -> reverse (reverse xs) === (xs :: [Int])' "OK, passed 100 tests."+dotest Test.QuickCheck 'quickCheck $ \xs -> reverse xs === (xs :: [Int])' "\[0,1\]"+echo+echo 'All tests passed!'
+ tests/CollectDataTypes.hs view
@@ -0,0 +1,199 @@+{-# LANGUAGE TemplateHaskell, RecordWildCards, DeriveLift, TupleSections, CPP, TypeOperators #-}+module CollectDataTypes where++import Language.Haskell.TH+import Language.Haskell.TH.Syntax+import Language.Haskell.TH.Ppr+import Language.Haskell.Interpreter hiding (lift)+import Data.Maybe+import Data.List+import System.Process+import Data.List.Split+import Control.Monad+import Control.Monad.Trans.Maybe+import Control.Monad.IO.Class+import Text.Printf+import Data.Either+import Data.Char+import Data.Function+import Test.QuickCheck+import Test.QuickCheck.Function++data DataType =+ DataType {+ dt_package :: String,+ dt_module :: String,+ dt_type :: String }+ deriving (Show, Lift)++getPackageDataTypes :: String -> IO [DataType]+getPackageDataTypes pkg = do+ mods <- filter isValidModule <$> getPackageModules pkg+ typess <- mapM getModuleDataTypes mods+ return [DataType pkg mod typ | (mod, types) <- zip mods typess, typ <- types]++getPackageModules :: String -> IO [String]+getPackageModules pkg =+ concatMap (parseWords . words) . splitOn ", " . unwords . words <$> readProcess cmd args ""+ where+ cmd:args = ["ghc-pkg", "field", pkg, "exposed-modules", "--simple-output"]+ parseWords [mod, "from", _] = [mod]+ parseWords xs = xs++getModuleDataTypes :: String -> IO [String]+getModuleDataTypes mod = do+ putStrLn mod+ Right names <- runInterpreter $ getModuleExports mod+ return [x | Data x _ <- names]++haskellName :: DataType -> String+haskellName DataType{..} = printf "%s.%s" dt_module (stripParens dt_type)+ where+ stripParens = reverse . dropWhile (== ')') . reverse . dropWhile (== '(')++dataTypeType :: DataType -> Q (Maybe Type)+dataTypeType dt = do+ mname <- lookupTypeName (haskellName dt)+ case mname of+ Nothing -> return Nothing+ Just name -> Just <$> reifyType name++typeArity :: Type -> Maybe Int+typeArity (AppT (AppT ArrowT StarT) kind) = succ <$> typeArity kind+typeArity StarT = return 0+typeArity _ = Nothing++createProperties :: String -> Q [Dec]+createProperties pkg = do+ datatypes0 <- runIO (getPackageDataTypes pkg)+ let datatypes = [ dt | dt <- datatypes0, not $ haskellName dt `elem` typeBlacklist ]+ let mkImport dt = printf "import %s -- for %s" (dt_module dt) (dt_type dt)+ missingModules <- fmap (map mkImport . nubBy ((==) `on` dt_module)) $ filterM (\ dt -> isNothing <$> dataTypeType dt) datatypes+ unless (null missingModules) $ error ("Missing the following imports:\n" ++ unlines missingModules)+ namesAndDecs <- fmap concat $ mapM createProperty datatypes+ let (allNames, props) = unzip namesAndDecs+ allPropsDec <- [d| allProps =+ $(pure $ ListE [ TupE [Just (LitE (StringL $ nameBase name)), Just (VarE name)]+ | name <- allNames ]+ )+ |]+ return $ allPropsDec ++ props++createProperty :: DataType -> Q [(Name, Dec)]+createProperty dt = do+ mtype <- dataTypeType dt+ -- TODO: monad?!+ case mtype of+ Nothing -> error $ "Can't find type in scope " ++ show dt+ Just typ -> case typeArity typ of+ Nothing -> pure []+ Just arity -> do+ Just name <- lookupTypeName (haskellName dt)+ Just int <- lookupTypeName "Int"+ nm <- newName ("prop_" ++ filter isAlphaNum (haskellName dt))+ nmCo <- newName ("prop_co_" ++ filter isAlphaNum (haskellName dt))+ nmFunction <- newName ("prop_function_" ++ filter isAlphaNum (haskellName dt))+ let propName = pure $ VarP nm+ let propNameCo = pure $ VarP nmCo+ let propNameFunction = pure $ VarP nmFunction+ let ty = pure $ foldl AppT (ConT name) $ replicate arity (ConT int)+ dArbitrary <- map (nm,) <$> [d| $propName = forAllBlind (arbitrary :: Gen $ty) (\ x -> x `seq` True) |]+ dCoArbitrary <- map (nmCo,) <$> [d| $propNameCo = forAllBlind (arbitrary :: Gen ($ty -> Integer)) (\ x -> x `seq` True) |]+ dFunction <- map (nmFunction,) <$> [d| $propNameFunction = forAllBlind (arbitrary :: Gen ($ty :-> Integer)) (\ x -> x `seq` True) |]+ return $ dArbitrary ++ dCoArbitrary ++ dFunction++typeBlacklist :: [String]+typeBlacklist = [ "Prelude.IO"+ , "Prelude.ReadS"+ , "Prelude.ShowS"+ , "System.IO.IO"+ , "System.IO.Error.IOError"+ , "Prelude.IOError"+ , "Data.Kind.Type"+ , "Data.Array.Byte.MutableByteArray"+ , "Data.IORef.IORef"+ , "Data.Kind.Constraint"+ , "Data.Unique.Unique"+ , "Data.STRef.STRef"+ , "Data.STRef.Lazy.STRef"+ , "Data.STRef.Strict.STRef"+ , "Data.Void.Void"+ , "Data.Proxy.KProxy"+ , "Data.Monoid.Endo"+ , "Data.Semigroup.Endo"+ , "Data.List.[]" -- This is buggy and annoying+ , "System.IO.HandlePosn"+ , "System.IO.Handle"+ , "Text.Printf.FieldFormatter" -- This is a function type and it+ -- requires an annoying coarbitrary instance+ , "Text.Printf.ModifierParser"+ , "Text.Show.ShowS"+ ] +++ -- These are phantom types used for indexing+ [ "Data.Fixed.E" ++ show i | i <- [0,1,2,3,6,9,12] ] +++#if MIN_VERSION_base(4,15,0)+ -- Exists but is deprecated+ [ "Data.Semigroup.Option" ] +++#endif+ -- TODO: Some controversial ones?+ [ "System.IO.Error.IOErrorType" ] +++ -- Some higher order types we ignore for the sake of CoArbitrary and Function issues+ [ "System.Console.GetOpt.OptDescr"+ , "System.Console.GetOpt.ArgOrder"+ , "System.Console.GetOpt.ArgDescr"+ ] +++ -- System specific and, likewise, not easily doable in Function+ [ "System.IO.TextEncoding" ] +++ -- Ignored for `Function` because the test monomorphises to `Int`. fixme.+ [ "Data.Complex.Complex" ]+++++modulePrefixBlacklist :: [String]+modulePrefixBlacklist = [ "GHC"+ , "Foreign"+ , "Control.Exception"+ -- Exports things like MVar etc+ , "Control.Concurrent"+ -- Exports ST and RealWorld that we can't support+ , "Control.Monad.ST"+ -- Existential wrapper around a Typeable thing, could+ -- be supported but would be a bit artificially+ -- limited to wrapping a bunch of types we can list+ , "Data.Dynamic"+ -- We _could_ support this, but it would result in+ -- the same problem as with Dynamic+ , "Data.Typeable"+ , "Type.Reflection"+ -- System.Mem.Weak and System.Mem.Stable export+ -- pointer types we don't support+ , "System.Mem"+ -- Exports an exception+ , "System.Timeout"+ -- Exports types, but not the constructors (or ways of+ -- creating them, e.g. Number). No feasible way to+ -- create meaningful generator+ , "Text.Read"+ -- Old generics implementation that doesn't fit nicely+ -- with arbitrary, as it has the same kind of problem+ -- that Typeable and Dynamic face+ , "Data.Data"+ -- Platform specific types that one could implement but+ -- would be tricky to keep consistent and correct+ -- across a number of platforms, esp. since we don't+ -- have good CI tests for them+ , "System.Posix"+ -- This exports a bunch of combinators whose only real+ -- role is to build a parser of an opaque type,+ -- consequently there isn't some super-interesting+ -- useful work you can do with it and it should be OK+ -- not to provide instances for it+ , "Text.ParserCombinators.ReadP"+ -- Slightly controversial, but this is only ignored for+ -- the sake of CoArbitrary and Function+ , "Data.Functor.Contravariant"+ ]++isValidModule :: String -> Bool+isValidModule mod = not $ any (`isPrefixOf` mod) modulePrefixBlacklist
+ tests/DiscardRatio.hs view
@@ -0,0 +1,56 @@+module Main where++import Control.Monad+import System.Exit+import Test.QuickCheck++assert :: String -> Bool -> IO ()+assert s False = do+ putStrLn $ s ++ " failed!"+ exitFailure+assert _ _ = return ()++quickCheckYesWith, quickCheckNoWith :: Testable p => Args -> p -> IO ()+quickCheckYesWith args p = do+ res <- quickCheckWithResult args p+ unless (isSuccess res) exitFailure+quickCheckNoWith args p = do+ res <- quickCheckWithResult args p+ when (isSuccess res) exitFailure+quickCheckYes, quickCheckNo :: Testable p => p -> IO ()+quickCheckYes = quickCheckYesWith stdArgs+quickCheckNo = quickCheckNoWith stdArgs++check :: Result -> Int -> Int -> IO ()+check res n d = do+ quickCheckYes $ once $ n === numTests res+ quickCheckYes $ once $ d === numDiscarded res++main :: IO ()+main = do+ putStrLn "Expecting gave up after 200 tries: False ==> True"+ res <- quickCheckResult $ withDiscardRatio 2 $ False ==> True+ check res 0 200+ res <- quickCheckWithResult stdArgs{maxDiscardRatio = 2} $ False ==> True+ check res 0 200++ putStrLn "\nExpecting success after 100 tests: x == x"+ res <- quickCheckResult $ withDiscardRatio 2 $ \ x -> (x :: Int) == x+ check res 100 0+ res <- quickCheckWithResult stdArgs{maxDiscardRatio = 2} $ \ x -> (x :: Int) == x+ check res 100 0++ -- The real ratio is 20, if 1 works or 40 doesn't it's+ -- probably because we broke something!+ let p50 = forAll (choose (1, 1000)) $ \ x -> (x :: Int) < 50 ==> True+ putStrLn "\nExpecting failure (discard ratio 1): x < 50 ==> True"+ quickCheckNo $ withDiscardRatio 1 p50+ quickCheckNoWith stdArgs{maxDiscardRatio = 1} p50+ putStrLn "\nExpecting success (discard ratio 40): x < 50 ==> True"+ quickCheckYes $ withDiscardRatio 40 p50+ quickCheckYesWith stdArgs{maxDiscardRatio = 40} p50++ -- Annoying interactions of discard and cover+ quickCheckYes $ forAllBlind (oneof [return True, return discard]) $ \ b -> cover 10 b "b" True+ quickCheck $ cover 10 discard "b" True+ quickCheck $ classify discard "b" True
+ tests/GCoArbitraryExample.hs view
@@ -0,0 +1,24 @@+{-# LANGUAGE DeriveGeneric, ScopedTypeVariables, TemplateHaskell #-}++module Main where++import GHC.Generics (Generic)+import Test.QuickCheck+import Test.QuickCheck.Function++data D a = C1 a | C2 deriving (Eq, Show, Read, Generic)+++instance Arbitrary a => Arbitrary (D a) where arbitrary = error "not implemented"+instance CoArbitrary a => CoArbitrary (D a)++instance (Show a, Read a) => Function (D a) where+ function = functionShow++prop_coarbitrary (Fun _ f) =+ expectFailure $+ withNumTests 1000 $+ f (C1 (2::Int)) `elem` [0, 1 :: Int]++return []+main = do True <- $quickCheckAll; return ()
+ tests/GShrinkExample.hs view
@@ -0,0 +1,20 @@+{-# LANGUAGE DeriveGeneric, ScopedTypeVariables, TemplateHaskell #-}++module Main where++import GHC.Generics (Generic)+import Test.QuickCheck++data Nat = Z | S Nat deriving (Eq, Show, Generic)+++instance Arbitrary Nat where arbitrary = error "not implemented"++prop_shrink =+ genericShrink (S (S Z)) === [S Z] .&&.+ genericShrink [0::Int] === [[]]++return []++main :: IO ()+main = do True <- $quickCheckAll; return ()
+ tests/Generators.hs view
@@ -0,0 +1,220 @@+{-# LANGUAGE TemplateHaskell, GeneralizedNewtypeDeriving, Rank2Types, NoMonomorphismRestriction #-}+import Test.QuickCheck+import Test.QuickCheck.Gen.Unsafe+import Data.List (inits, sort, nub)+import Data.Int+import Data.Word+import Data.Version+import System.Exit+import Data.Complex+import Text.ParserCombinators.ReadP (readP_to_S)++newtype Path a = Path [a] deriving (Show, Functor)++instance Arbitrary a => Arbitrary (Path a) where+ arbitrary = do+ x <- arbitrary+ fmap Path (pathFrom 100 x)+ where+ pathFrom n x =+ fmap (x:) $+ case shrink x of+ [] -> return []+ _ | n == 0 -> return []+ ys -> oneof [pathFrom (n-1) y | y <- ys]++ shrink (Path xs) = map Path [ ys | ys <- inits xs, length ys > 0 && length ys < length xs ]++path :: (a -> Bool) -> Path a -> Bool+path p (Path xs) = all p xs++somePath :: (a -> Bool) -> Path a -> Property+somePath p = expectFailure . withNumTests 1000 . path (not . p)++newtype Extremal a = Extremal { getExtremal :: a } deriving (Show, Eq, Ord, Num, Enum, Real, Integral)++instance (Arbitrary a, Bounded a) => Arbitrary (Extremal a) where+ arbitrary =+ fmap Extremal $+ frequency+ [(1, return minBound),+ (1, return maxBound),+ (8, arbitrary)]+ shrink (Extremal x) = map Extremal (shrink x)++smallProp :: Integral a => Path a -> Bool+smallProp = path (\x -> (x >= -100 || -100 `asTypeOf` x >= 0) && x <= 100)++largeProp :: Integral a => Path a -> Property+largeProp = somePath (\x -> x < -1000000 || x > 1000000)++prop_int :: Path Int -> Bool+prop_int = smallProp++prop_int32 :: Path Int32 -> Property+prop_int32 = largeProp++prop_word :: Path Word -> Property+prop_word = largeProp++prop_word32 :: Path Word32 -> Property+prop_word32 = largeProp++prop_integer :: Path Integer -> Bool+prop_integer = smallProp++prop_small :: Path (Small Int) -> Bool+prop_small = smallProp++prop_large :: Path (Large Int) -> Property+prop_large = largeProp++prop_smallWord :: Path (Small Word) -> Bool+prop_smallWord = smallProp++prop_largeWord :: Path (Large Word) -> Property+prop_largeWord = largeProp++data Choice a b = Choice a b deriving Show+instance (Arbitrary a, Arbitrary b) => Arbitrary (Choice a b) where+ arbitrary = do+ Capture eval <- capture+ return (Choice (eval arbitrary) (eval arbitrary))++idemProp :: (Eq a, Arbitrary a, Arbitrary b) => (b -> a) -> Choice a b -> Bool+idemProp f (Choice x y) = x == f y++prop_fixed_length :: Arbitrary a => Path (Fixed a) -> Bool+prop_fixed_length (Path xs) = length xs == 1++prop_fixed_idem = idemProp getFixed+prop_blind_idem = idemProp getBlind++prop_ordered_list = path (\(Ordered xs) -> sort xs == xs)+prop_nonempty_list = path (\(NonEmpty xs) -> not (null xs))++pathInt, somePathInt ::+ (Arbitrary (f (Extremal Int)), Show (f (Extremal Int)),+ Arbitrary (f Integer), Show (f Integer),+ Arbitrary (f (Extremal Int8)), Show (f (Extremal Int8)),+ Arbitrary (f (Extremal Int16)), Show (f (Extremal Int16)),+ Arbitrary (f (Extremal Int32)), Show (f (Extremal Int32)),+ Arbitrary (f (Extremal Int64)), Show (f (Extremal Int64)),+ Arbitrary (f (Extremal Word)), Show (f (Extremal Word)),+ Arbitrary (f (Extremal Word8)), Show (f (Extremal Word8)),+ Arbitrary (f (Extremal Word16)), Show (f (Extremal Word16)),+ Arbitrary (f (Extremal Word32)), Show (f (Extremal Word32)),+ Arbitrary (f (Extremal Word64)), Show (f (Extremal Word64))) =>+ Bool -> (forall a. f a -> a) -> (forall a. Integral a => a -> Bool) -> Property+pathInt word f p =+ conjoin+ [counterexample "Int" (path ((p :: Int -> Bool) . getExtremal . f)),+ counterexample "Integer" (path ((p :: Integer -> Bool) . f)),+ counterexample "Int8" (path ((p :: Int8 -> Bool) . getExtremal . f)),+ counterexample "Int16" (path ((p :: Int16 -> Bool) . getExtremal . f)),+ counterexample "Int32" (path ((p :: Int32 -> Bool) . getExtremal . f)),+ counterexample "Int64" (path ((p :: Int64 -> Bool) . getExtremal . f)),+ counterexample "Word" (not word .||. path ((p :: Word -> Bool) . getExtremal . f)),+ counterexample "Word8" (not word .||. path ((p :: Word8 -> Bool) . getExtremal . f)),+ counterexample "Word16" (not word .||. path ((p :: Word16 -> Bool) . getExtremal . f)),+ counterexample "Word32" (not word .||. path ((p :: Word32 -> Bool) . getExtremal . f)),+ counterexample "Word64" (not word .||. path ((p :: Word64 -> Bool) . getExtremal . f))]+somePathInt word f p = expectFailure (pathInt word f (not . p))++prop_positive = pathInt True getPositive (> 0)+prop_positive_bound = somePathInt True getPositive (== 1)++prop_nonzero = pathInt True getNonZero (/= 0)+prop_nonzero_bound_1 = somePathInt True getNonZero (== 1)+prop_nonzero_bound_2 = somePathInt True getNonZero (== -1)++prop_nonnegative = pathInt True getNonNegative (>= 0)+prop_nonnegative_bound = somePathInt True getNonNegative (== 0)++prop_negative = pathInt False getNegative (< 0)+prop_negative_bound = somePathInt False getNegative (== -1)++prop_nonpositive = pathInt True getNonPositive (<= 0)+prop_nonpositive_bound = somePathInt True getNonPositive (== 0)++reachesBound :: (Bounded a, Integral a, Arbitrary a) =>+ a -> Property+reachesBound x = withNumTests 1000 (expectFailure (x < 3 * (maxBound `div` 4)))++prop_reachesBound_Int8 = reachesBound :: Int8 -> Property+prop_reachesBound_Int16 = reachesBound :: Int16 -> Property+prop_reachesBound_Int32 = reachesBound :: Int32 -> Property+prop_reachesBound_Int64 = reachesBound :: Int64 -> Property+prop_reachesBound_Word8 = reachesBound :: Word8 -> Property+prop_reachesBound_Word16 = reachesBound :: Word16 -> Property+prop_reachesBound_Word32 = reachesBound :: Word32 -> Property+prop_reachesBound_Word64 = reachesBound :: Word64 -> Property++-- Shrinking should not loop.+noShrinkingLoop :: (Eq a, Arbitrary a) => Path a -> Bool+noShrinkingLoop (Path (x:xs)) = x `notElem` xs++prop_no_shrinking_loop_Unit = noShrinkingLoop :: Path () -> Bool+prop_no_shrinking_loop_Bool = noShrinkingLoop :: Path Bool -> Bool+prop_no_shrinking_loop_Ordering = noShrinkingLoop :: Path Ordering -> Bool+prop_no_shrinking_loop_Maybe = noShrinkingLoop :: Path (Maybe Int) -> Bool+prop_no_shrinking_loop_Either = noShrinkingLoop :: Path (Either Int Int) -> Bool+prop_no_shrinking_loop_List = noShrinkingLoop :: Path [Int] -> Bool+prop_no_shrinking_loop_Ratio = noShrinkingLoop :: Path Rational -> Bool+prop_no_shrinking_loop_Complex = noShrinkingLoop :: Path (Complex Double) -> Bool+prop_no_shrinking_loop_Fixed = noShrinkingLoop :: Path (Fixed Int) -> Bool+prop_no_shrinking_loop_Pair = noShrinkingLoop :: Path (Int, Int) -> Bool+prop_no_shrinking_loop_Triple = noShrinkingLoop :: Path (Int, Int, Int) -> Bool+prop_no_shrinking_loop_Integer = noShrinkingLoop :: Path Integer -> Bool+prop_no_shrinking_loop_Int = noShrinkingLoop :: Path Int -> Bool+prop_no_shrinking_loop_Int8 = noShrinkingLoop :: Path Int8 -> Bool+prop_no_shrinking_loop_Int16 = noShrinkingLoop :: Path Int16 -> Bool+prop_no_shrinking_loop_Int32 = noShrinkingLoop :: Path Int32 -> Bool+prop_no_shrinking_loop_Int64 = noShrinkingLoop :: Path Int64 -> Bool+prop_no_shrinking_loop_Word = noShrinkingLoop :: Path Word -> Bool+prop_no_shrinking_loop_Word8 = noShrinkingLoop :: Path Word8 -> Bool+prop_no_shrinking_loop_Word16 = noShrinkingLoop :: Path Word16 -> Bool+prop_no_shrinking_loop_Word32 = noShrinkingLoop :: Path Word32 -> Bool+prop_no_shrinking_loop_Word64 = noShrinkingLoop :: Path Word64 -> Bool+prop_no_shrinking_loop_Char = noShrinkingLoop :: Path Char -> Bool+prop_no_shrinking_loop_Float = noShrinkingLoop :: Path Float -> Bool+prop_no_shrinking_loop_Double = noShrinkingLoop :: Path Double -> Bool+prop_no_shrinking_loop_Version = noShrinkingLoop :: Path Version -> Bool+prop_no_shrinking_loop_ExitCode = noShrinkingLoop :: Path ExitCode -> Bool++-- Check that shrinking a Double always produces a shrinking candidate.+prop_shrink_candidate_double :: Property+prop_shrink_candidate_double =+ forAllShrink gen shrink $ \x ->+ x > 0 ==>+ not (null (shrink x))+ where+ gen :: Gen Double+ gen = oneof [arbitrary, fmap fromInteger arbitrary]++-- Bad shrink: infinite list+--+-- remove unexpectedFailure in prop_B1, shrinking should not loop forever.+data B1 = B1 Int deriving (Eq, Show)++instance Arbitrary B1 where+ arbitrary = fmap B1 arbitrary+ shrink x = x : shrink x++prop_B1 :: B1 -> Property+prop_B1 (B1 n) = expectFailure $ n === n + 1++-- Double properties:++-- We occasionaly generate duplicates.+prop_double_duplicate_list :: [Double] -> Property+prop_double_duplicate_list xs = expectFailure $ nub xs === xs where+ sorted = sort xs++-- And enough numbers to show basic IEEE pit falls.+prop_double_assoc :: Double -> Double -> Double -> Property+prop_double_assoc x y z = expectFailure $ x + (y + z) === (x + y) + z+++return []+main = do True <- $forAllProperties (quickCheckWithResult stdArgs { maxShrinks = 10000 }); return ()
+ tests/Misc.hs view
@@ -0,0 +1,50 @@+-- Miscellaneous tests.++{-# LANGUAGE TemplateHaskell #-}+import Test.QuickCheck+import Test.QuickCheck.Random+import Data.Map+import Control.Monad++prop_verbose :: Blind (Int -> Int -> Bool) -> Property+prop_verbose (Blind p) =+ forAll (mkQCGen <$> arbitrary) $ \g ->+ ioProperty $ do+ res1 <- quickCheckWithResult stdArgs{replay = Just (g, 100), chatty = False} p+ res2 <- quickCheckWithResult stdArgs{replay = Just (g, 100), chatty = False} (verbose p)+ return $+ numTests res1 === numTests res2 .&&.+ failingTestCase res1 === failingTestCase res2++prop_failingTestCase :: Blind (Int -> Int -> Int -> Bool) -> Property+prop_failingTestCase (Blind p) = ioProperty $ do+ res <- quickCheckWithResult stdArgs{chatty = False} p+ let [x, y, z] = failingTestCase res+ return (not (p (read x) (read y) (read z)))++prop_maxSize :: Property+prop_maxSize = withMaxSize 10 (forAll (arbitrary :: Gen Int) $ \ x -> abs x < 10)++prop_cover :: Property+prop_cover = withNumTests 1000+ $ checkCoverage+ $ forAll (arbitrary :: Gen Int)+ $ \ x -> cover 5 (x > 0) "positive" True++-- Issue #382+prop_discardCoverage :: Property+prop_discardCoverage = checkCoverage $ forAll (sized $ \ n -> pure n) $ \ x -> cover 10 True "label" $ x /= 99 ==> True++return []+main = do+ True <- $quickCheckAll+ Success{classes=cls} <- quickCheckResult $ classify False "A" $ classify True "B" True+ [("A",0),("B",100)] <- return $ toList cls+ Success{numTests=1000} <- quickCheckResult prop_cover+ forM_ [const discard, const [discard], \ x -> discard : shrink x] $ \ shr -> do+ Failure{reason="Falsified"} <- quickCheckResult $ forAllShrink arbitrary shr (odd :: Int -> Bool)+ return ()+ -- These shouldn't crash+ sample (discard :: Gen Int)+ sample (oneof [discard, return 1] :: Gen Int)+ sample (oneof [return (1, discard), return (1, 1)] :: Gen (Int, Int))
+ tests/MonadFix.hs view
@@ -0,0 +1,26 @@+{-# LANGUAGE TemplateHaskell, RecursiveDo #-}+import Test.QuickCheck+import Control.Monad.Fix++-- A simple (not complete) test for the MonadFix instance.+cyclicList :: Gen [Int]+cyclicList = do+ rec xs <- fmap (:ys) arbitrary+ ys <- fmap (:xs) arbitrary+ return xs++prop_cyclic :: Property+prop_cyclic =+ forAll (Blind <$> cyclicList) $ \(Blind xs) ->+ -- repeats with period 2+ and $ take 100 $ zipWith (==) xs (drop 2 xs)++prop_period2 :: Property+prop_period2 =+ expectFailure $+ forAll (Blind <$> cyclicList) $ \(Blind xs) ->+ -- does not always repeat with period 1+ and $ take 100 $ zipWith (==) xs (drop 1 xs)++return []+main = do True <- $quickCheckAll; return ()
+ tests/Monoids.hs view
@@ -0,0 +1,146 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE ConstraintKinds #-}++#if __GLASGOW_HASKELL__ >= 800+{-# OPTIONS_GHC -Wno-orphans #-}+#endif+{-# LANGUAGE FlexibleInstances #-}++module Main (main) where++#ifndef NO_SEMIGROUP_CLASS+import Data.List.NonEmpty+import Data.Semigroup (Semigroup (..))+#else+import Data.Monoid (Monoid (..), (<>))+#endif++import Test.QuickCheck++#ifdef NO_SEMIGROUP_CLASS+type Semigroup = Monoid+sconcat :: Monoid a => [a] -> a+sconcat = mconcat+#endif++instance Arbitrary Every where+ arbitrary = oneof [ pure $ Every True+ , pure $ Every False+ , pure $ Every (counterexample "False" False)+ , pure $ Every (counterexample "True" True)+ , pure $ Every (ioProperty (return True))+ , pure $ Every (ioProperty (return False))+ , pure $ Every (checkCoverage $ cover 100 True "" True)+ , pure $ Every (checkCoverage $ cover 100 True "" False)+ , pure $ Every (checkCoverage $ cover 100 False "" False)+ ]+++instance Arbitrary Some where+ arbitrary = oneof [ pure $ Some True+ , pure $ Some False+ , pure $ Some (counterexample "False" False)+ , pure $ Some (counterexample "True" True)+ , pure $ Some (ioProperty (return True))+ , pure $ Some (ioProperty (return False))+ , pure $ Some (checkCoverage $ cover 100 True "" True)+ , pure $ Some (checkCoverage $ cover 100 True "" False)+ , pure $ Some (checkCoverage $ cover 100 False "" True)+ , pure $ Some (checkCoverage $ cover 100 False "" False)+ ]+++newtype Fail a = Fail a++instance Arbitrary (Fail Every) where+ arbitrary = oneof [ Fail <$> (arbitrary :: Gen Every)+ , pure $ Fail $ Every (checkCoverage $ cover 100 False "" True)+ ]+++check_associative_law :: (Testable p, Semigroup p) => Blind p -> Blind p -> Blind p -> Property+check_associative_law (Blind a) (Blind b) (Blind c) = ioProperty $ do+ x <- isSuccess <$> quickCheckWithResult args (a <> (b <> c))+ y <- isSuccess <$> quickCheckWithResult args ((a <> b) <> c)+ return (x == y)+++#ifndef NO_SEMIGROUP_SUPERCLASS+check_unit_law :: (Testable p, Monoid p) => Blind p -> Property+#else+check_unit_law :: (Testable p, Monoid p, Semigroup p) => Blind p -> Property+#endif+check_unit_law (Blind a) = ioProperty $ do+ x <- isSuccess <$> quickCheckWithResult args (a <> mempty)+ y <- isSuccess <$> quickCheckWithResult args (mempty <> a)+ z <- isSuccess <$> quickCheckWithResult args a+ return (x == y .&&. y == z)+++#ifndef NO_SEMIGROUP_CLASS+check_sconcat_law :: (Testable p, Semigroup p) => Blind p -> Blind p -> Property+check_sconcat_law (Blind a) (Blind b) = ioProperty $ do+ x <- isSuccess <$> quickCheckWithResult args (sconcat $ a :| [b])+ y <- isSuccess <$> quickCheckWithResult args (a <> b)+ return (x == y)+#endif+++#ifndef NO_SEMIGROUP_SUPERCLASS+check_mconcat_law :: (Testable p, Monoid p) => Blind p -> Blind p -> Property+#else+check_mconcat_law :: (Testable p, Monoid p, Semigroup p) => Blind p -> Blind p -> Property+#endif+check_mconcat_law (Blind a) (Blind b) = ioProperty $ do+ x <- isSuccess <$> quickCheckWithResult args (mconcat [a, b])+ y <- isSuccess <$> quickCheckWithResult args (a <> b)+ return (x == y)+++--+-- Auxiliary definitions+--++args :: Args+args = stdArgs { chatty = False, maxShrinks = 0 }++--+-- Properties+--++prop_every_associative :: Blind Every -> Blind Every -> Blind Every -> Property+prop_every_associative = check_associative_law++prop_every_unit :: Blind Every -> Property+prop_every_unit = check_unit_law++prop_every_unit_fail :: Blind (Fail Every) -> Property+prop_every_unit_fail (Blind (Fail a)) =+ expectFailure $ check_unit_law (Blind a)++#ifndef NO_SEMIGROUP_CLASS+prop_every_sconcat_law :: Blind Every -> Blind Every -> Property+prop_every_sconcat_law = check_sconcat_law+#endif++prop_every_mconcat_law :: Blind Every -> Blind Every -> Property+prop_every_mconcat_law = check_mconcat_law++prop_some_associative :: Blind Some -> Blind Some -> Blind Some -> Property+prop_some_associative = check_associative_law++prop_some_unit :: Blind Some -> Property+prop_some_unit = check_unit_law++#ifndef NO_SEMIGROUP_CLASS+prop_some_sconcat_law :: Blind Some -> Blind Some -> Property+prop_some_sconcat_law = check_sconcat_law+#endif++prop_some_mconcat_law :: Blind Some -> Blind Some -> Property+prop_some_mconcat_law = check_mconcat_law++return []+main = do True <- $quickCheckAll; return ()+
+ tests/RunCollectDataTypes.hs view
@@ -0,0 +1,89 @@+{-# LANGUAGE TemplateHaskell, CPP #-}+{-# OPTIONS_GHC -ddump-splices #-}+import CollectDataTypes+import Language.Haskell.TH+import Test.QuickCheck.All+import Test.QuickCheck++import Control.Applicative+import Control.Arrow+import Control.Concurrent+import Control.Concurrent.Chan+import Control.Concurrent.MVar+import Control.Concurrent.QSem+import Control.Concurrent.QSemN+import Control.Exception+import Control.Exception.Base+import Control.Monad.ST+import Control.Monad.ST.Lazy+import Control.Monad.ST.Strict+#if defined(MIN_VERSION_base)+#if MIN_VERSION_base(4,17,0)+import Data.Array.Byte+#endif+#endif+import Data.Bits+import Data.Bool+import Data.Char+import Data.Complex+import Data.Data+import Data.Dynamic+import Data.Either+import Data.Fixed+import Data.Functor.Compose+import Data.Functor.Const+import Data.Functor.Contravariant+import Data.Functor.Identity+import Data.Functor.Product+import Data.Functor.Sum+import Data.IORef+import Data.Int+import Data.Kind+import Data.List.NonEmpty+import Data.Maybe+import Data.Monoid+import Data.Ord+import Data.Proxy+import Data.Ratio+import Data.STRef+import Data.STRef.Lazy+import Data.STRef.Strict+import Data.Semigroup+import Data.String+import Data.Tuple+import Data.Type.Bool+import Data.Type.Coercion+import Data.Type.Equality+#if defined(MIN_VERSION_base)+#if MIN_VERSION_base(4,16,0)+import Data.Type.Ord+#endif+#endif+import Data.Typeable+import Data.Unique+import Data.Version+import Data.Void+import Data.Word+import Numeric.Natural+import System.Console.GetOpt+import System.Exit+import System.IO+import System.IO.Error+import System.Mem.StableName+import System.Mem.Weak+import System.Posix.Internals+import System.Posix.Types+import System.Timeout+import Text.ParserCombinators.ReadP+import Text.ParserCombinators.ReadPrec+import Text.Printf+import Text.Read+import Text.Read.Lex+import Text.Show+import Type.Reflection+import Type.Reflection.Unsafe+import Unsafe.Coerce++$(createProperties "base")++main = runQuickCheckAll allProps quickCheckResult
+ tests/Split.hs view
@@ -0,0 +1,28 @@+import Test.QuickCheck+import Test.QuickCheck.Random+import Data.List (group, isPrefixOf, sort)++-- This type allows us to run integerVariant and get a list of bits out.+newtype Splits = Splits { unSplits :: [Bool] } deriving (Eq, Ord, Show)++instance Splittable Splits where+ left (Splits xs) = Splits (xs ++ [False])+ right (Splits xs) = Splits (xs ++ [True])++-- Check that integerVariant gives a prefix-free code,+-- i.e., if m /= n then integerVariant m is not a prefix of integerVariant n.+prop_split_prefix :: Property+prop_split_prefix =+ once $ forAllShrink (return [-10000..10000]) shrink $ \ns ->+ map head (group (sort ns)) == sort ns ==> -- no duplicates+ let+ codes :: [Splits]+ codes = sort [integerVariant n (Splits []) | n <- ns]++ ok (Splits xs) (Splits ys) = not (xs `isPrefixOf` ys)+ in+ -- After sorting, any prefix will end up immediately before+ -- one of its suffixes+ and (zipWith ok codes (drop 1 codes))++main = do Success{} <- quickCheckResult prop_split_prefix; return ()
+ tests/Strictness.hs view
@@ -0,0 +1,42 @@+-- Strictness tests.++{-# LANGUAGE CPP, TemplateHaskell #-}+import Test.QuickCheck++import Control.Exception (Exception (..), throw)++#if MIN_VERSION_containers(0,5,0)+import Control.Exception (evaluate, try)+import Data.Map.Strict (Map)+import qualified Data.Map.Strict as Map+#endif++data Thunk++instance Arbitrary Thunk where+ arbitrary = throw ThunkError++instance Show Thunk where+ show _ = "Thunk"++data ThunkError = ThunkError deriving Show++instance Exception ThunkError++prop_strictMap :: Property+#if MIN_VERSION_containers(0,5,0)+prop_strictMap = again . ioProperty $ do+ m <- generate arbitrary+ result <- try $ evaluate m :: IO (Either ThunkError (Map Int Thunk))+ pure $ case result of+ Right _ | not (Map.null m) -> counterexample ("Thunks in Map: " ++ show m) False+ _ -> property True+#else+prop_strictMap = once $ property True+#endif++return []+main :: IO ()+main = do+ True <- $quickCheckAll+ return ()
+ tests/Terminal.hs view
@@ -0,0 +1,94 @@+-- Check that the terminal output works correctly.+{-# LANGUAGE TemplateHaskell, DeriveGeneric #-}+import Test.QuickCheck+import Test.QuickCheck.Text+import System.Process+import System.IO+import Control.Exception+import GHC.Generics+import Control.DeepSeq++data Command =+ PutPart String+ | PutLine String+ | PutTemp String+ deriving (Eq, Ord, Show, Generic)++instance Arbitrary Command where+ arbitrary =+ oneof [+ PutPart <$> line,+ PutLine <$> line,+ PutTemp <$> line]+ where+ line = filter (/= '\n') <$> arbitrary+ shrink = genericShrink++exec :: Terminal -> Command -> IO ()+exec tm (PutPart xs) = putPart tm xs+exec tm (PutLine xs) = putLine tm xs+exec tm (PutTemp xs) = putTemp tm xs++eval :: [Command] -> String+eval = concatMap eval1+ where+ eval1 (PutPart xs) = xs+ eval1 (PutLine xs) = xs ++ "\n"+ -- PutTemp only has an effect on stderr+ eval1 (PutTemp xs) = ""++-- Evaluate the result of printing a given string, taking backspace+-- characters into account.+format :: String -> String+format xs = format1 [] [] xs+ where+ -- Arguments: text before the cursor (in reverse order),+ -- text after the cursor, text to print+ format1 xs ys [] = line xs ys+ -- \n emits a new line+ format1 xs ys ('\n':zs) = line xs ys ++ "\n" ++ format1 [] [] zs+ -- \b moves the cursor to the left+ format1 (x:xs) ys ('\b':zs) = format1 xs (x:xs) zs+ -- beginning of line: \b ignored+ format1 [] ys ('\b':zs) = format1 [] ys zs+ -- Normal printing puts the character before the cursor,+ -- and overwrites the next character after the cursor+ format1 xs ys (z:zs) = format1 (z:xs) (drop 1 ys) zs++ line xs ys = reverse xs ++ ys++-- Check that the terminal satisfies the following properties:+-- * The text written to stdout matches what's returned by terminalOutput+-- * The output agrees with the model implementation 'eval'+-- * Anything written to stderr (presumably by putTemp) is erased+prop_terminal :: [Command] -> Property+prop_terminal cmds =+ withNumTests 1000 $ ioProperty $+ withPipe $ \stdout_read stdout_write ->+ withPipe $ \stderr_read stderr_write -> do+ out <- withHandleTerminal stdout_write (Just stderr_write) $ \tm -> do+ mapM_ (exec tm) (cmds ++ [PutPart ""])+ terminalOutput tm+ stdout <- stdout_read+ stderr <- stderr_read+ return $ conjoin [+ counterexample "output == terminalOutput" $ stdout === out,+ counterexample "output == model" $ out === eval cmds,+ counterexample "putTemp erased" $ all (== ' ') (format stderr) ]+ where+ withPipe :: (IO String -> Handle -> IO a) -> IO a+ withPipe action = do+ (readh, writeh) <- createPipe+ hSetEncoding readh utf8+ hSetEncoding writeh utf8+ let+ read = do+ hClose writeh+ contents <- hGetContents readh+ return $!! contents+ action read writeh `finally` do+ hClose readh+ hClose writeh++return []+main = do True <- $quickCheckAll; return ()
+ tests/WithProgress.hs view
@@ -0,0 +1,68 @@+import Test.QuickCheck+import Test.QuickCheck.Monadic++import Data.IORef+import System.Exit (exitSuccess, exitFailure)+import Control.Monad (when)++main :: IO ()+main = do+ succ <- newIORef 0+ disc <- newIORef 0++ -- test 1 + quickCheck (propTestWithProgress succ)+ i <- readIORef succ+ when (i /= 99) exitFailure++ modifyIORef succ (const 0)++ -- test 2+ quickCheck (proptestWithProgressDiscard succ disc)+ i1 <- readIORef succ+ i2 <- readIORef disc+ putStrLn $ show i1+ putStrLn $ show i2+ when (i1 /= 0 || i2 /= 999) exitFailure++ modifyIORef succ (const 0)+ modifyIORef disc (const 0)++ -- test 3+ quickCheck (proptestWithProgressNotInstalled succ disc)+ i1 <- readIORef succ+ i2 <- readIORef disc+ putStrLn $ show i1+ putStrLn $ show i2+ when (i1 /= 0 || i2 /= 0) exitFailure++ exitSuccess++-- all these tests succeed, incrementing the counter every time+propTestWithProgress :: IORef Int -> Property+propTestWithProgress ref =+ forAll (arbitrary :: Gen Int) $ \n ->+ withProgress (\p -> modifyIORef ref (\_ -> currentPassed p))+ $ n == n++-- all of these tests are discarded, never updating the currentPassed counter, but always+-- the currentDiscarded one.+proptestWithProgressDiscard :: IORef Int -> IORef Int -> Property+proptestWithProgressDiscard succ disc =+ forAll (arbitrary :: Gen Int) $ \n ->+ withProgress (\p -> do+ modifyIORef succ (\_ -> currentPassed p)+ modifyIORef disc (\_ -> currentDiscarded p))+ $ n > 1000 ==>+ n == n++-- The callback is installed after the test on n, meaning that since all tests are+-- discarded, it will never be called. It will never be executed.+proptestWithProgressNotInstalled :: IORef Int -> IORef Int -> Property+proptestWithProgressNotInstalled succ disc =+ forAll (arbitrary :: Gen Int) $ \n ->+ n > 1000 ==>+ withProgress (\p -> do+ modifyIORef succ (\_ -> currentPassed p)+ modifyIORef disc (\_ -> currentDiscarded p))+ $ n == n