quickcheck-classes 0.3.1 → 0.6.5.0
raw patch · 19 files changed
Files
- README.md +93/−0
- Setup.hs +0/−2
- changelog.md +228/−0
- quickcheck-classes.cabal +169/−23
- src/Test/QuickCheck/Classes.hs +118/−849
- src/Test/QuickCheck/Classes/Alt.hs +63/−0
- src/Test/QuickCheck/Classes/Apply.hs +65/−0
- src/Test/QuickCheck/Classes/Euclidean.hs +122/−0
- src/Test/QuickCheck/Classes/IsList.hs +8/−0
- src/Test/QuickCheck/Classes/Json.hs +68/−0
- src/Test/QuickCheck/Classes/MVector.hs +354/−0
- src/Test/QuickCheck/Classes/Plus.hs +93/−0
- src/Test/QuickCheck/Classes/Prim.hs +390/−0
- src/Test/QuickCheck/Classes/Ring.hs +43/−0
- src/Test/QuickCheck/Classes/Semigroupoid.hs +81/−0
- src/Test/QuickCheck/Classes/Semiring.hs +191/−0
- test/Advanced.hs +193/−0
- test/Spec.hs +224/−29
- test/Spec/ShowRead.hs +121/−0
README.md view
@@ -1,1 +1,94 @@ # quickcheck-classes++This library provides sets of properties that should hold for common typeclasses,+along with three (3) simple functions that you can use to test them.++### `lawsCheck`:++A convenience function for testing properties in GHCi.+For example, at GHCi:++```bash+>>> lawsCheck (monoidLaws (Proxy :: Proxy Ordering))+Monoid: Associative +++ OK, passed 100 tests.+Monoid: Left Identity +++ OK, passed 100 tests.+Monoid: Right Identity +++ OK, passed 100 tests.+```++Assuming that the `Arbitrary` instance for `Ordering` is good, we now+have confidence that the `Monoid` instance for `Ordering` satisfies+the monoid laws.++### `lawsCheckMany`:++A convenience function for checking multiple typeclass instances+of multiple types. Consider the following Haskell source file:++```haskell+import Data.Proxy (Proxy(..))+import Data.Map (Map)+import Data.Set (Set)++-- A 'Proxy' for 'Set' 'Int'. +setInt :: Proxy (Set Int)+setInt = Proxy++-- A 'Proxy' for 'Map' 'Int' 'Int'.+mapInt :: Proxy (Map Int Int)+mapInt = Proxy++myLaws :: Proxy a -> [Laws]+myLaws p = [eqLaws p, monoidLaws p]++namedTests :: [(String, [Laws])]+namedTests =+ [ ("Set Int", myLaws setInt)+ , ("Map Int Int", myLaws mapInt)+ ]+```++Now, in GHCi:++```bash+>>> lawsCheckMany namedTests++Testing properties for common typeclasses+-------------+-- Set Int --+-------------++Eq: Transitive +++ OK, passed 100 tests.+Eq: Symmetric +++ OK, passed 100 tests.+Eq: Reflexive +++ OK, passed 100 tests.+Monoid: Associative +++ OK, passed 100 tests.+Monoid: Left Identity +++ OK, passed 100 tests.+Monoid: Right Identity +++ OK, passed 100 tests.+Monoid: Concatenation +++ OK, passed 100 tests.++-----------------+-- Map Int Int --+-----------------++Eq: Transitive +++ OK, passed 100 tests.+Eq: Symmetric +++ OK, passed 100 tests.+Eq: Reflexive +++ OK, passed 100 tests.+Monoid: Associative +++ OK, passed 100 tests.+Monoid: Left Identity +++ OK, passed 100 tests.+Monoid: Right Identity +++ OK, passed 100 tests.+Monoid: Concatenation +++ OK, passed 100 tests.++```++### `lawsCheckOne`++A convenience function that allows one to check many typeclass+instances of the same type.++For example, in GHCi:++```bash+>>> lawsCheckOne (Proxy :: Proxy Word) [jsonLaws, showReadLaws]+ToJSON/FromJSON: Encoding Equals Value +++ OK, passed 100 tests.+ToJSON/FromJSON: Partial Isomorphism +++ OK, passed 100 tests.+Show/Read: Partial Isomorphism +++ OK, passed 100 tests.+```
− Setup.hs
@@ -1,2 +0,0 @@-import Distribution.Simple-main = defaultMain
+ changelog.md view
@@ -0,0 +1,228 @@+# Changelog+All notable changes to this project will be documented in this file.++The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)+and this project adheres to the [Haskell Package Versioning Policy](https://pvp.haskell.org/).++Note that since `quickcheck-classes` reexports larges parts of+`quickcheck-classes-base`, changelog entries that deal with any of the+classes from `base` are duplicated across the two changelogs.++## [0.6.5.0] - 2021-04-12+### Added+- Laws for `abs` and `signum`+- Storable Set-Set Law (resolves issue 101).+- Add laws for `quotRem` and `divMod`.+- Use non-commutative monoid for bifoldable tests (resolves issue 98)+- `substitutiveEqLaws`, which tests for Eq substitutivity.+- Negation law check for `Eq`.+- Document that users can provide their own `Laws`.++## [0.6.4.0] - 2019-09-13+### Changed+- Use newer semirings++## [0.6.3.0] - 2019-08-08+### Added+- `gcdDomainLaws`+- `euclideanLaws`+### Changed+- Replaces 0.6.2.2. That release should have been a minor version+ bump since it added new features.+- Support `primitive-0.6.4.0`.+- Extend `semiringLaws` to cover `fromNatural`+- Factor out a subset of laws tests into `quickcheck-classes-base`+ and depend on this library.++## [0.6.2.2] - 2019-06-18+### Added+- `numLaws`+- `bitraversableLaws`++## [0.6.2.1] - 2019-05-23+### Fixed+- Removal of BadList test that was causing the test suite to fail++## [0.6.2.0] - 2019-05-23+### Added+- `ixLaws`+- `contravariantLaws`+- `semigroupMonoidLaws`+### Changed+- extend `mvectorLaws`+- extend `applyLaws` to include associativity+### Fixed+- bug in `foldableLaws` which could fail to catch implementations of `foldMap` or `fold`+ that evaluate in the wrong order++## [0.6.1.0] - 2019-01-12+### Change+- `genericLaws` and `generic1Laws` were not exported. Now they are.+### Added+- Add `muvectorLaws`.++## [0.6.0.0] - 2018-12-24+### Change+- Support QuickCheck 2.7 and 2.8. This adds `Arbitrary` orphan instances+ to the test suite.+- Fix CPP that caused build failures on GHC 7.10 and some old+ package versions.+- Fix compiling the test suite without semigroupoids and compiling with old+ versions of transformers.+- Add lower bound for semigroups to make sure the `stimes` method is available.+- The laws `commutativeSemigroupLaws` and `commutativeMonoidLaws` no longer+ check any property other than commutativity. They must now be used in conjunction+ with, rather than in place of, `semigroupLaws` and `monoidLaws`. This is a breaking+ change.+- Fix the right distribution law for semirings.+- The function `lawsCheckMany` now terminates with exit code 1 if a+ test fails.+- Extend `showReadLaws` with new properties for `showsPrec`, `readsPrec`,+ `showList` and `readList`.+- Prettify JSON partial isomorphism test failure.+### Added+- Add `genericLaws` and `generic1Laws`+- Add property tests for special classes of semigroups. This includes:+ commutative, idempotent, rectangular band, and exponential.+- `bifoldableLaws`, `bifoldableFunctorLaws`+- Add `showLaws`.++## [0.5.0.0] - 2018-09-25+### Change+- When compiling with GHC 8.6 and newer, use `QuantifiedConstraints` instead+ of `Eq1`, `Show1`, `Arbitrary1`, `Eq2`, `Show`, and `Arbitrary2`.++## [0.4.14.3] - 2018-09-21+### Change+- Fix a CPP conditional import problem that caused build failures on GHC 7.10+- Set an explicit lower bound for containers++## [0.4.14.2] - 2018-09-12+### Change+- Support QuickCheck-2.12+- Fix compilation for containers<0.5.9+- Fix compilation with QuickCheck-2.9++## [0.4.14.1] - 2018-07-24+### Change+- Build correctly when dependency on semigroupoids is disabled.++## [0.4.14] - 2018-07-23+### Added+- commutativeSemigroupLaws+- the following typeclasses:+ `Data.Semigroupoid.Semigroupoid` (semigroupoids)+ `Data.Functor.Plus.Plus` (semigroupoids)++### Change+- semiringLaws were never exported, we now export them.+- make documentation for `MonadPlus` and `Alternative` consistent.+- bump semirings to 0.2.0.0+- deprecate `Test.QuickCheck.Classes.specialisedLawsCheckMany`+ in favour of `Test.QuickCheck.Classes.lawsCheckOne`++## [0.4.13] - 2018-07-18+### Added+- Laws for `Enum` typeclass.+- Laws for `Category` typeclass.++## [0.4.12] - 2018-06-07+### Added+- Remaining laws for `Storable` typeclass.+- Laws for `Prim` typeclass requiring `setByteArray` and `setOffAddr` to+ match the behavior that would result from manually iterating over the+ array and writing the value element-by-element.+### Change+- Correct the law from the `Bits` typeclass that relates `clearBit`+ and `zeroBits`.+- Limit the size of the lists that are used when testing that+ `mconcat` and `sconcat` have behaviors that match their default+ implementations. For some data structures, concatenating the+ elements in a list of several dozen arbitrary values does not+ finish in a reasonable amount of time. So, the size of these+ has been limited to 6.+- Make library build against `primitive-0.6.1.0`.++## [0.4.11.1] - 2018-05-25+### Change+- Fix compatibility with older GHCs when `semigroupoids` support+ is disabled.++## [0.4.11] - 2018-05-24+### Added+- Greatly improved documentation+- `specialisedLawsCheckMany` function, a shorter way for the user+ to use `lawsCheckMany` on a single type.++### Change+- Some internal names, making it more clear what it is that they do.++## [0.4.10] - 2018-05-03+### Added+- Property tests for `mconcat`, `sconcat`, and `stimes`. It isn't+ common to override the defaults for these, but when you do, it's+ nice to check that they agree with what they are supposed to do.++## [0.4.9] - 2018-04-06+### Change+- Be more careful with import of `Data.Primitive`. There is a+ branch of `primitive` that adds `PrimArray`. The implementation+ of `PrimArray` in this library should eventually be removed, but+ for now it will be sufficient to ensure that it does not create+ a conflicting import problem with the one in the branch.++## [0.4.8] - 2018-03-29+### Change+- Fix compilation regression for older versions of transformers.++## [0.4.7] - 2018-03-29+### Change+- Split up monolithic module into hidden internal modules.+- Fix compilation regression for older GHCs.++## [0.4.6] - 2018-03-29+### Added+- Property test the naturality law for `MonadZip`. There is another law+ that instances should satisfy (the Information Preservation law), but+ it's more difficult to write a test for. It has been omitted for now.+- Property tests for all `MonadPlus` laws.+- Several additional property tests for list-like containers: mapMaybe,+ replicate, filter.++## [0.4.5] - 2018-03-26+### Added+- Property tests for list-like containers that have `IsList` instances.+ These are useful for things that are nearly `Foldable` or nearly `Traversable`+ but are either constrained in their element type or totally monomorphic+ in it.++## [0.4.4] - 2018-03-23+### Added+- Cabal flags for controlling whether or not `aeson` and `semigroupoids`+ are used. These are mostly provided to accelerate builds `primitive`'s+ test suite.++## [0.4.3] - 2018-03-23+### Added+- Property tests for `foldl1` and `foldr1`.+- Property tests for `Traversable`.++## [0.4.2] - 2018-03-22+### Changed+- Made compatible with `transformers-0.3`. Tests for higher-kinded+ typeclasses are unavailable when built with a sufficiently old+ version of both `transformers` and `base`. This is because `Eq1`+ and `Show1` are unavailable in this situation.++## [0.4.1] - 2018-03-21+### Changed+- Made compatible with `transformers-0.4`.++## [0.4.0] - 2018-03-20+### Added+- Property tests for `Bifunctor` and `Alternative`.+### Changed+- Made compatible with older GHCs all the way back to 7.8.4.+- Lower dependency footprint. Eliminate the dependency on `prim-array`+ and inline the relevant functions and types from it into+ `Test.QuickCheck.Classes`. None of these are exported.
quickcheck-classes.cabal view
@@ -1,51 +1,197 @@+cabal-version: 2.4 name: quickcheck-classes-version: 0.3.1+version: 0.6.5.0 synopsis: QuickCheck common typeclasses description:- This library provides quickcheck properties to- ensure that typeclass instances the set of laws that they are- supposed to. There are other libraries that do similar- things, such as `genvalidity-hspec` and `checkers`. This- library differs from other solutions by not introducing- any new typeclasses that the user needs to learn. The source- code for this library should be easy to understand if you- are already familiar with quickcheck. Open an issue- if you feel that this is not the case.+ This library provides QuickCheck properties to ensure+ that typeclass instances adhere to the set of laws that+ they are supposed to. There are other libraries that do+ similar things, such as `genvalidity-hspec` and `checkers`.+ This library differs from other solutions by not introducing+ any new typeclasses that the user needs to learn.+ .+ /Note:/ on GHC < 8.5, this library uses the higher-kinded typeclasses+ ('Data.Functor.Classes.Show1', 'Data.Functor.Classes.Eq1', 'Data.Functor.Classes.Ord1', etc.),+ but on GHC >= 8.5, it uses `-XQuantifiedConstraints` to express these+ constraints more cleanly. homepage: https://github.com/andrewthad/quickcheck-classes#readme-license: BSD3+license: BSD-3-Clause license-file: LICENSE-author: Andrew Martin+author: Andrew Martin, chessai maintainer: andrew.thaddeus@gmail.com-copyright: 2017 Andrew Martin+copyright: 2018 Andrew Martin category: Testing build-type: Simple extra-source-files: README.md-cabal-version: >=1.10+extra-source-files: changelog.md +flag aeson+ description:+ You can disable the use of the `aeson` package using `-f-aeson`.+ .+ This may be useful for accelerating builds in sandboxes for expert users.+ default: True+ manual: True++flag semigroupoids+ description:+ You can disable the use of the `semigroupoids` package using `-f-semigroupoids`.+ .+ This may be useful for accelerating builds in sandboxes for expert users.+ default: True+ manual: True++flag semirings+ description:+ You can disable the use of the `semirings` package using `-f-semirings`.+ .+ This may be useful for accelerating builds in sandboxes for expert users.+ default: True+ manual: True++flag vector+ description:+ You can disable the use of the `vector` package using `-f-vector`.+ .+ This may be useful for accelerating builds in sandboxes for expert users.+ default: True+ manual: True++flag unary-laws+ description:+ Include infrastructure for testing class laws of unary type constructors.+ It is required that this flag match the value that the `unary-laws` flag+ was given when building `quickcheck-classes-base`.+ default: True+ manual: True++flag binary-laws+ description:+ Include infrastructure for testing class laws of binary type constructors.+ It is required that this flag match the value that the `unary-laws` flag+ was given when building `quickcheck-classes-base`. Disabling `unary-laws`+ while keeping `binary-laws` enabled is an unsupported configuration.+ default: True+ manual: True+ library+ default-language: Haskell2010 hs-source-dirs: src exposed-modules: Test.QuickCheck.Classes+ Test.QuickCheck.Classes.IsList+ other-modules:+ Test.QuickCheck.Classes.Alt+ Test.QuickCheck.Classes.Apply+ Test.QuickCheck.Classes.Euclidean+ Test.QuickCheck.Classes.Json+ Test.QuickCheck.Classes.MVector+ Test.QuickCheck.Classes.Plus+ Test.QuickCheck.Classes.Prim+ Test.QuickCheck.Classes.Semigroupoid+ Test.QuickCheck.Classes.Semiring+ Test.QuickCheck.Classes.Ring build-depends:- base >= 4.7 && < 5+ , base >= 4.5 && < 5+ , QuickCheck >= 2.7+ , transformers >= 0.3 && < 0.6+ , primitive >= 0.6.4 && < 0.8+ , primitive-addr >= 0.1.0.2 && < 0.2+ , containers >= 0.4.2.1+ , quickcheck-classes-base >=0.6.2 && <0.7+ if impl(ghc < 8.0)+ build-depends:+ , semigroups >= 0.17+ , fail+ if impl(ghc < 7.8)+ build-depends: tagged+ if impl(ghc > 7.4) && impl(ghc < 7.6)+ build-depends: ghc-prim+ if impl(ghc > 8.5)+ cpp-options: -DHAVE_QUANTIFIED_CONSTRAINTS+ if flag(unary-laws)+ build-depends:+ , transformers >= 0.4.0+ , QuickCheck >= 2.10.0+ cpp-options: -DHAVE_UNARY_LAWS+ if flag(binary-laws)+ build-depends:+ , transformers >= 0.5.0+ , QuickCheck >= 2.10.0+ cpp-options: -DHAVE_BINARY_LAWS+ if flag(aeson)+ build-depends: aeson >= 0.9+ cpp-options: -DHAVE_AESON+ if flag(semigroupoids)+ build-depends: semigroupoids+ cpp-options: -DHAVE_SEMIGROUPOIDS+ if flag(semirings)+ build-depends: semirings >= 0.4.2+ cpp-options: -DHAVE_SEMIRINGS+ if flag(vector)+ build-depends: vector >= 0.12+ cpp-options: -DHAVE_VECTOR++-- The basic test suite is compatible with all the versions of GHC that+-- this library supports. It is useful for confirming whether the laws tests+-- behave correct. Additionally, it helps catch CPP mistakes.+test-suite basic+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: Spec.hs+ other-modules:+ Spec.ShowRead+ build-depends:+ , base+ , base-orphans >= 0.5+ , quickcheck-classes , QuickCheck- , transformers+ , containers , primitive- , prim-array- , aeson+ , vector+ , transformers+ , tagged+ if impl(ghc > 8.5)+ cpp-options: -DHAVE_QUANTIFIED_CONSTRAINTS+ if flag(unary-laws)+ cpp-options: -DHAVE_UNARY_LAWS+ if flag(binary-laws)+ cpp-options: -DHAVE_BINARY_LAWS+ if flag(aeson)+ build-depends: aeson+ cpp-options: -DHAVE_AESON+ if flag(semigroupoids)+ build-depends: semigroupoids+ cpp-options: -DHAVE_SEMIGROUPOIDS+ if flag(vector)+ build-depends: vector >= 0.12+ cpp-options: -DHAVE_VECTOR default-language: Haskell2010 -test-suite test+-- The advanced test suite only builds with the newest version+-- of GHC. It is intended to be a sort of regression test for GHC and for+-- base. It check instances for a number of types in base. It also checks+-- a bunch of derived instances for data types of varying sizes. And it+-- does some tests on UnboxedSums.+test-suite advanced type: exitcode-stdio-1.0 hs-source-dirs: test- main-is: Spec.hs+ main-is: Advanced.hs+ ghc-options: -O2 build-depends:- base- , quickcheck-classes , QuickCheck+ , base >= 4.12+ , base-orphans >= 0.5+ , containers , primitive- , aeson+ , quickcheck-classes+ , tagged+ , tasty+ , tasty-quickcheck+ , transformers , vector+ if impl(ghc < 8.6)+ buildable: False default-language: Haskell2010 source-repository head
src/Test/QuickCheck/Classes.hs view
@@ -1,871 +1,140 @@-{-# LANGUAGE BangPatterns #-} {-# LANGUAGE CPP #-}-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE MagicHash #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE KindSignatures #-} {-# OPTIONS_GHC -Wall #-} -{-|--This library provides lists of properties that should hold for common typeclasses.-All of these take a 'Proxy' argument that is used to nail down the type for which-the typeclass dictionaries should be tested. For example, at GHCi:-->>> lawsCheck (monoidLaws (Proxy :: Proxy Ordering))-Monoid: Associative +++ OK, passed 100 tests.-Monoid: Left Identity +++ OK, passed 100 tests.-Monoid: Right Identity +++ OK, passed 100 tests.--Assuming that the 'Arbitrary' instance for 'Ordering' is good, we now-have confidence that the 'Monoid' instance for 'Ordering' satisfies-the monoid laws. We can check multiple typeclasses with:-->>> foldMap (lawsCheck . ($ (Proxy :: Proxy Word))) [jsonLaws,showReadLaws]-ToJSON/FromJSON: Encoding Equals Value +++ OK, passed 100 tests.-ToJSON/FromJSON: Partial Isomorphism +++ OK, passed 100 tests.-Show/Read: Partial Isomorphism +++ OK, passed 100 tests.+{-| This library provides sets of properties that should hold for common+ typeclasses. + /Note:/ on GHC < 8.6, this library uses the higher-kinded typeclasses+ ('Data.Functor.Classes.Show1', 'Data.Functor.Classes.Eq1', 'Data.Functor.Classes.Ord1', etc.),+ but on GHC >= 8.6, it uses @-XQuantifiedConstraints@ to express these+ constraints more cleanly. -} module Test.QuickCheck.Classes ( -- * Running- lawsCheck- , lawsCheckMany+ QCB.lawsCheck+ , QCB.lawsCheckMany+ , QCB.lawsCheckOne -- * Properties- -- ** Ground Types- , semigroupLaws- , monoidLaws- , commutativeMonoidLaws- , eqLaws- , ordLaws- , showReadLaws+ -- ** Ground types+#if MIN_VERSION_base(4,7,0)+ , QCB.bitsLaws+#endif+ , QCB.eqLaws+ , QCB.substitutiveEqLaws+ , QCB.numLaws+ , QCB.integralLaws+ , QCB.ixLaws+#if MIN_VERSION_base(4,7,0)+ , QCB.isListLaws+#endif+#if HAVE_AESON , jsonLaws- , isListLaws+#endif+ , QCB.monoidLaws+ , QCB.commutativeMonoidLaws+ , QCB.semigroupMonoidLaws+ , QCB.ordLaws+ , QCB.enumLaws+ , QCB.boundedEnumLaws , primLaws- , storableLaws-#if MIN_VERSION_QuickCheck(2,10,0)- -- ** Higher-Kinded Types- , functorLaws- , applicativeLaws- , monadLaws- , foldableLaws+ , QCB.semigroupLaws+ , QCB.commutativeSemigroupLaws+ , QCB.exponentialSemigroupLaws+ , QCB.idempotentSemigroupLaws+ , QCB.rectangularBandSemigroupLaws+#if HAVE_SEMIRINGS+ , semiringLaws+ , ringLaws+ , gcdDomainLaws+ , euclideanLaws #endif+ , QCB.showLaws+ , QCB.showReadLaws+ , QCB.storableLaws+#if MIN_VERSION_base(4,5,0)+ , QCB.genericLaws+ , QCB.generic1Laws+#endif+#if HAVE_UNARY_LAWS+ -- ** Unary type constructors+ , QCB.alternativeLaws+#if HAVE_SEMIGROUPOIDS+ , altLaws+ , applyLaws+#endif+ , QCB.applicativeLaws+ , QCB.contravariantLaws+ , QCB.foldableLaws+ , QCB.functorLaws+ , QCB.monadLaws+ , QCB.monadPlusLaws+ , QCB.monadZipLaws+#if HAVE_SEMIGROUPOIDS+ , plusLaws+ , extendedPlusLaws+#endif+ , QCB.traversableLaws+#endif+#if HAVE_BINARY_LAWS+ -- ** Binary type constructors+ , QCB.bifoldableLaws+ , QCB.bifunctorLaws+ , QCB.bitraversableLaws+ , QCB.categoryLaws+ , QCB.commutativeCategoryLaws+#if HAVE_SEMIGROUPOIDS+ , semigroupoidLaws+ , commutativeSemigroupoidLaws+#endif+#if HAVE_VECTOR+ , muvectorLaws+#endif+#endif -- * Types- , Laws(..)+ , QCB.Laws(..)+ , QCB.Proxy1(..)+ , QCB.Proxy2(..) ) where -import Test.QuickCheck-import Test.QuickCheck.Monadic (monadicIO)-import Test.QuickCheck.Property (Property(..))-import Data.Primitive hiding (sizeOf,newArray,copyArray)-import Data.Primitive.PrimArray-import Data.Proxy-import Control.Monad.ST-import Control.Monad-import Data.Monoid (Endo(..),Sum(..),Dual(..))-import GHC.Ptr (Ptr(..))-import Data.Primitive.Addr (Addr(..))-import Foreign.Marshal.Alloc-import System.IO.Unsafe-import Data.Semigroup (Semigroup)-import GHC.Exts (IsList(fromList,toList,fromListN),Item)-import Foreign.Marshal.Array-import Foreign.Storable-import Text.Read (readMaybe)-import Data.Aeson (FromJSON(..),ToJSON(..))-import Data.Functor.Classes-import Control.Applicative-import Data.Foldable (foldlM,fold,foldMap,foldl',foldr')-import Control.Exception (ErrorCall,evaluate,try)-import Control.Monad.Trans.Class (lift)-import qualified Data.Foldable as F-import qualified Data.Aeson as AE-import qualified Data.Primitive as P-import qualified Data.Semigroup as SG-import qualified GHC.OldList as L--#if MIN_VERSION_QuickCheck(2,10,0)-import Test.QuickCheck.Arbitrary (Arbitrary1(..))-#endif---- | A set of laws associated with a typeclass.-data Laws = Laws- { lawsTypeclass :: String- -- ^ Name of the typeclass whose laws are tested- , lawsProperties :: [(String,Property)]- -- ^ Pairs of law name and property- }---- | A convenience function for working testing properties in GHCi.--- See the test suite of this library for an example of how to--- integrate multiple properties into larger test suite.-lawsCheck :: Laws -> IO ()-lawsCheck (Laws className properties) = do- flip foldlMapM properties $ \(name,p) -> do- putStr (className ++ ": " ++ name ++ " ")- quickCheck p---- | A convenience function for checking multiple typeclass instances--- of multiple types.-lawsCheckMany ::- [(String,[Laws])] -- ^ Element is type name paired with typeclass laws- -> IO ()-lawsCheckMany xs = do- putStrLn "Testing properties for common typeclasses"- r <- flip foldlMapM xs $ \(typeName,laws) -> do- putStrLn $ "------------"- putStrLn $ "-- " ++ typeName- putStrLn $ "------------"- flip foldlMapM laws $ \(Laws typeClassName properties) -> do- flip foldlMapM properties $ \(name,p) -> do- putStr (typeClassName ++ ": " ++ name ++ " ")- r <- quickCheckResult p- return $ case r of- Success _ _ _ -> Good- _ -> Bad- putStrLn ""- case r of- Good -> putStrLn "All tests succeeded"- Bad -> putStrLn "One or more tests failed"--data Status = Bad | Good--instance Monoid Status where- mempty = Good- mappend Good x = x- mappend Bad _ = Bad--foldlMapM :: (Foldable t, Monoid b, Monad m) => (a -> m b) -> t a -> m b-foldlMapM f = foldlM (\b a -> fmap (mappend b) (f a)) mempty--jsonLaws :: (ToJSON a, FromJSON a, Show a, Arbitrary a, Eq a) => Proxy a -> Laws-jsonLaws p = Laws "ToJSON/FromJSON"- [ ("Encoding Equals Value", jsonEncodingEqualsValue p)- , ("Partial Isomorphism", jsonEncodingPartialIsomorphism p)- ]---- | Tests the following properties: ----- [/Partial Isomorphism/]--- @fromList . toList ≡ id@--- [/Length Preservation/]--- @fromList xs ≡ fromListN (length xs) xs@-isListLaws :: (IsList a, Show a, Show (Item a), Arbitrary a, Arbitrary (Item a), Eq a) => Proxy a -> Laws-isListLaws p = Laws "IsList"- [ ("Partial Isomorphism", isListPartialIsomorphism p)- , ("Length Preservation", isListLengthPreservation p)- ]--showReadLaws :: (Show a, Read a, Eq a, Arbitrary a) => Proxy a -> Laws-showReadLaws p = Laws "Show/Read"- [ ("Partial Isomorphism", showReadPartialIsomorphism p)- ]---- | Tests the following properties:+-- re-exports ----- [/Associative/]--- @a <> (b <> c) ≡ (a <> b) <> c@-semigroupLaws :: (Semigroup a, Eq a, Arbitrary a, Show a) => Proxy a -> Laws-semigroupLaws p = Laws "Semigroup"- [ ("Associative", semigroupAssociative p)- ] --- | Tests the following properties:------ [/Transitive/]--- @a == b ∧ b == c ⇒ a == c@--- [/Symmetric/]--- @a == b ⇒ b == a@--- [/Reflexive/]--- @a == a@------ Some of these properties involve implication. In the case that--- the left hand side of the implication arrow does not hold, we--- do not retry. Consequently, these properties only end up being--- useful when the data type has a small number of inhabitants.-eqLaws :: (Eq a, Arbitrary a, Show a) => Proxy a -> Laws-eqLaws p = Laws "Eq"- [ ("Transitive", eqTransitive p)- , ("Symmetric", eqSymmetric p)- , ("Reflexive", eqReflexive p)- ]---- | Tests the following properties:------ [/Transitive/]--- @a ≤ b ∧ b ≤ c ⇒ a ≤ c@--- [/Comparable/]--- @a ≤ b ∨ a > b@-ordLaws :: (Ord a, Arbitrary a, Show a) => Proxy a -> Laws-ordLaws p = Laws "Ord"- [ ("Transitive", ordTransitive p)- , ("Comparable", ordComparable p)- ]---- | Tests the following properties:------ [/Associative/]--- @mappend a (mappend b c) ≡ mappend (mappend a b) c@--- [/Left Identity/]--- @mappend mempty a ≡ a@--- [/Right Identity/]--- @mappend a mempty ≡ a@-monoidLaws :: (Monoid a, Eq a, Arbitrary a, Show a) => Proxy a -> Laws-monoidLaws p = Laws "Monoid"- [ ("Associative", monoidAssociative p)- , ("Left Identity", monoidLeftIdentity p)- , ("Right Identity", monoidRightIdentity p)- ]---- | Tests everything from 'monoidProps' plus the following:------ [/Commutative/]--- @mappend a b ≡ mappend b a@-commutativeMonoidLaws :: (Monoid a, Eq a, Arbitrary a, Show a) => Proxy a -> Laws-commutativeMonoidLaws p = Laws "Commutative Monoid" $ lawsProperties (monoidLaws p) ++- [ ("Commutative", monoidCommutative p)- ]---- | Test that a 'Prim' instance obey the several laws.-primLaws :: (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Laws-primLaws p = Laws "Prim"- [ ("ByteArray Set-Get (you get back what you put in)", primSetGetByteArray p)- , ("ByteArray Get-Set (putting back what you got out has no effect)", primGetSetByteArray p)- , ("ByteArray Set-Set (setting twice is same as setting once)", primSetSetByteArray p)- , ("ByteArray List Conversion Roundtrips", primListByteArray p)- , ("Addr Set-Get (you get back what you put in)", primSetGetAddr p)- , ("Addr Get-Set (putting back what you got out has no effect)", primGetSetAddr p)- , ("Addr List Conversion Roundtrips", primListAddr p)- ]--storableLaws :: (Storable a, Eq a, Arbitrary a, Show a) => Proxy a -> Laws-storableLaws p = Laws "Storable"- [ ("Set-Get (you get back what you put in)", storableSetGet p)- , ("Get-Set (putting back what you got out has no effect)", storableGetSet p)- , ("List Conversion Roundtrips", storableList p)- ]--isListPartialIsomorphism :: forall a. (IsList a, Show a, Arbitrary a, Eq a) => Proxy a -> Property-isListPartialIsomorphism _ = myForAllShrink False- (\(a :: a) -> ["a = " ++ show a])- "fromList (toList a)"- (\a -> fromList (toList a))- "a"- (\a -> a)--isListLengthPreservation :: forall a. (IsList a, Show (Item a), Arbitrary (Item a), Eq a) => Proxy a -> Property-isListLengthPreservation _ = property $ \(xs :: [Item a]) ->- (fromList xs :: a) == fromListN (length xs) xs--showReadPartialIsomorphism :: forall a. (Show a, Read a, Arbitrary a, Eq a) => Proxy a -> Property-showReadPartialIsomorphism _ = property $ \(a :: a) ->- readMaybe (show a) == Just a---- TODO: improve the quality of the error message if--- something does not pass this test.-jsonEncodingEqualsValue :: forall a. (ToJSON a, Show a, Arbitrary a) => Proxy a -> Property-jsonEncodingEqualsValue _ = property $ \(a :: a) ->- case AE.decode (AE.encode a) of- Nothing -> False- Just (v :: AE.Value) -> v == toJSON a--jsonEncodingPartialIsomorphism :: forall a. (ToJSON a, FromJSON a, Show a, Eq a, Arbitrary a) => Proxy a -> Property-jsonEncodingPartialIsomorphism _ = property $ \(a :: a) ->- AE.decode (AE.encode a) == Just a--eqTransitive :: forall a. (Show a, Eq a, Arbitrary a) => Proxy a -> Property-eqTransitive _ = property $ \(a :: a) b c -> case a == b of- True -> case b == c of- True -> a == c- False -> a /= c- False -> case b == c of- True -> a /= c- False -> True---- Technically, this tests something a little stronger than it is supposed to.--- But that should be alright since this additional strength is implied by--- the rest of the Ord laws.-ordTransitive :: forall a. (Show a, Ord a, Arbitrary a) => Proxy a -> Property-ordTransitive _ = property $ \(a :: a) b c -> case (compare a b, compare b c) of- (LT,LT) -> a < c- (LT,EQ) -> a < c- (LT,GT) -> True- (EQ,LT) -> a < c- (EQ,EQ) -> a == c- (EQ,GT) -> a > c- (GT,LT) -> True- (GT,EQ) -> a > c- (GT,GT) -> a > c--ordComparable :: forall a. (Show a, Ord a, Arbitrary a) => Proxy a -> Property-ordComparable _ = property $ \(a :: a) b -> a > b || b >= a--eqSymmetric :: forall a. (Show a, Eq a, Arbitrary a) => Proxy a -> Property-eqSymmetric _ = property $ \(a :: a) b -> case a == b of- True -> b == a- False -> b /= a--eqReflexive :: forall a. (Show a, Eq a, Arbitrary a) => Proxy a -> Property-eqReflexive _ = property $ \(a :: a) -> a == a--semigroupAssociative :: forall a. (Semigroup a, Eq a, Arbitrary a, Show a) => Proxy a -> Property-semigroupAssociative _ = property $ \(a :: a) b c -> a SG.<> (b SG.<> c) == (a SG.<> b) SG.<> c--monoidAssociative :: forall a. (Monoid a, Eq a, Arbitrary a, Show a) => Proxy a -> Property-monoidAssociative _ = myForAllShrink True- (\(a :: a,b,c) -> ["a = " ++ show a, "b = " ++ show b, "c = " ++ show c])- "mappend a (mappend b c)"- (\(a,b,c) -> mappend a (mappend b c))- "mappend (mappend a b) c"- (\(a,b,c) -> mappend (mappend a b) c)--monoidLeftIdentity :: forall a. (Monoid a, Eq a, Arbitrary a, Show a) => Proxy a -> Property-monoidLeftIdentity _ = myForAllShrink False- (\(a :: a) -> ["a = " ++ show a])- "mappend mempty a"- (\a -> mappend mempty a)- "a"- (\a -> a)--monoidRightIdentity :: forall a. (Monoid a, Eq a, Arbitrary a, Show a) => Proxy a -> Property-monoidRightIdentity _ = myForAllShrink False- (\(a :: a) -> ["a = " ++ show a])- "mappend a mempty"- (\a -> mappend a mempty)- "a"- (\a -> a)--monoidCommutative :: forall a. (Monoid a, Eq a, Arbitrary a, Show a) => Proxy a -> Property-monoidCommutative _ = property $ \(a :: a) b -> mappend a b == mappend b a--primListByteArray :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property-primListByteArray _ = property $ \(as :: [a]) ->- as == toList (fromList as :: PrimArray a)--primListAddr :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property-primListAddr _ = property $ \(as :: [a]) -> unsafePerformIO $ do- let len = L.length as- ptr@(Ptr addr#) :: Ptr a <- mallocBytes (len * P.sizeOf (undefined :: a))- let addr = Addr addr#- let go :: Int -> [a] -> IO ()- go !ix xs = case xs of- [] -> return ()- (x : xsNext) -> do- writeOffAddr addr ix x- go (ix + 1) xsNext- go 0 as- let rebuild :: Int -> IO [a]- rebuild !ix = if ix < len- then (:) <$> readOffAddr addr ix <*> rebuild (ix + 1)- else return []- asNew <- rebuild 0- free ptr- return (as == asNew)--primSetGetByteArray :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property-primSetGetByteArray _ = property $ \(a :: a) len -> (len > 0) ==> do- ix <- choose (0,len - 1)- return $ runST $ do- arr <- newPrimArray len- writePrimArray arr ix a- a' <- readPrimArray arr ix- return (a == a')--primGetSetByteArray :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property-primGetSetByteArray _ = property $ \(as :: [a]) -> (not (L.null as)) ==> do- let arr1 = fromList as :: PrimArray a- len = L.length as- ix <- choose (0,len - 1)- arr2 <- return $ runST $ do- marr <- newPrimArray len- copyPrimArray marr 0 arr1 0 len- a <- readPrimArray marr ix- writePrimArray marr ix a- unsafeFreezePrimArray marr- return (arr1 == arr2)--primSetSetByteArray :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property-primSetSetByteArray _ = property $ \(a :: a) (as :: [a]) -> (not (L.null as)) ==> do- let arr1 = fromList as :: PrimArray a- len = L.length as- ix <- choose (0,len - 1)- (arr2,arr3) <- return $ runST $ do- marr2 <- newPrimArray len- copyPrimArray marr2 0 arr1 0 len- writePrimArray marr2 ix a- marr3 <- newPrimArray len- copyMutablePrimArray marr3 0 marr2 0 len- arr2 <- unsafeFreezePrimArray marr2- writePrimArray marr3 ix a- arr3 <- unsafeFreezePrimArray marr3- return (arr2,arr3)- return (arr2 == arr3)--primSetGetAddr :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property-primSetGetAddr _ = property $ \(a :: a) len -> (len > 0) ==> do- ix <- choose (0,len - 1)- return $ unsafePerformIO $ do- ptr@(Ptr addr#) :: Ptr a <- mallocBytes (len * P.sizeOf (undefined :: a))- let addr = Addr addr#- writeOffAddr addr ix a- a' <- readOffAddr addr ix- free ptr- return (a == a')--primGetSetAddr :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property-primGetSetAddr _ = property $ \(as :: [a]) -> (not (L.null as)) ==> do- let arr1 = fromList as :: PrimArray a- len = L.length as- ix <- choose (0,len - 1)- arr2 <- return $ unsafePerformIO $ do- ptr@(Ptr addr#) :: Ptr a <- mallocBytes (len * P.sizeOf (undefined :: a))- let addr = Addr addr#- copyPrimArrayToPtr ptr arr1 0 len- a :: a <- readOffAddr addr ix- writeOffAddr addr ix a- marr <- newPrimArray len- copyPtrToMutablePrimArray marr 0 ptr len- free ptr- unsafeFreezePrimArray marr- return (arr1 == arr2)--storableSetGet :: forall a. (Storable a, Eq a, Arbitrary a, Show a) => Proxy a -> Property-storableSetGet _ = property $ \(a :: a) len -> (len > 0) ==> do- ix <- choose (0,len - 1)- return $ unsafePerformIO $ do- ptr :: Ptr a <- mallocArray len- pokeElemOff ptr ix a- a' <- peekElemOff ptr ix- free ptr- return (a == a')--storableGetSet :: forall a. (Storable a, Eq a, Arbitrary a, Show a) => Proxy a -> Property-storableGetSet _ = property $ \(as :: [a]) -> (not (L.null as)) ==> do- let len = L.length as- ix <- choose (0,len - 1)- return $ unsafePerformIO $ do- ptrA <- newArray as- ptrB <- mallocArray len- copyArray ptrB ptrA len- a <- peekElemOff ptrA ix- pokeElemOff ptrA ix a- res <- arrayEq ptrA ptrB len- free ptrA- free ptrB- return res--storableList :: forall a. (Storable a, Eq a, Arbitrary a, Show a) => Proxy a -> Property-storableList _ = property $ \(as :: [a]) -> unsafePerformIO $ do- let len = L.length as- ptr <- newArray as- let rebuild :: Int -> IO [a]- rebuild !ix = if ix < len- then (:) <$> peekElemOff ptr ix <*> rebuild (ix + 1)- else return []- asNew <- rebuild 0- free ptr- return (as == asNew)--arrayEq :: forall a. (Storable a, Eq a) => Ptr a -> Ptr a -> Int -> IO Bool-arrayEq ptrA ptrB len = go 0 where- go !i = if i < len- then do- a <- peekElemOff ptrA i- b <- peekElemOff ptrB i- if a == b- then go (i + 1)- else return False- else return True--#if MIN_VERSION_QuickCheck(2,10,0)--- | Tests the following applicative properties:------ [/Identity/]--- @'fmap' 'id' ≡ 'id'@--- [/Composition/]--- @fmap (f . g) ≡ 'fmap' f . 'fmap' g@--- [/Const/]--- @(<$) ≡ 'fmap' 'const'@-functorLaws :: (Functor f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Laws-functorLaws p = Laws "Functor"- [ ("Identity", functorIdentity p)- , ("Composition", functorComposition p)- , ("Const", functorConst p)- ]---- | Tests the following applicative properties:------ [/Identity/]--- @'pure' 'id' '<*>' v ≡ v@--- [/Composition/]--- @'pure' (.) '<*>' u '<*>' v '<*>' w ≡ u '<*>' (v '<*>' w)@--- [/Homomorphism/]--- @'pure' f '<*>' 'pure' x ≡ 'pure' (f x)@--- [/Interchange/]--- @u '<*>' 'pure' y ≡ 'pure' ('$' y) '<*>' u@--- [/LiftA2 (1)/]--- @('<*>') ≡ 'liftA2' 'id'@-applicativeLaws :: (Applicative f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Laws-applicativeLaws p = Laws "Applicative"- [ ("Identity", applicativeIdentity p)- , ("Composition", applicativeComposition p)- , ("Homomorphism", applicativeHomomorphism p)- , ("Interchange", applicativeInterchange p)- , ("LiftA2 Part 1", applicativeLiftA2_1 p)- -- todo: liftA2 part 2, we need an equation of two variables for this- ]----- | Tests the following monadic properties:------ [/Left Identity/]--- @'return' a '>>=' k ≡ k a@--- [/Right Identity/]--- @m '>>=' 'return' ≡ m@--- [/Associativity/]--- @m '>>=' (\\x -> k x '>>=' h) ≡ (m '>>=' k) '>>=' h@--- [/Return/]--- @'pure' ≡ 'return'@--- [/Ap/]--- @('<*>') ≡ 'ap'@-monadLaws :: (Monad f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Laws-monadLaws p = Laws "Monad"- [ ("Left Identity", monadLeftIdentity p)- , ("Right Identity", monadRightIdentity p)- , ("Associativity", monadAssociativity p)- , ("Return", monadReturn p)- , ("Ap", monadAp p)- ]---- | Tests the following 'Foldable' properties:------ [/fold/]--- @'fold' ≡ 'foldMap' 'id'@--- [/foldMap/]--- @'foldMap' f ≡ 'foldr' ('mappend' . f) 'mempty'@--- [/foldr/]--- @'foldr' f z t ≡ 'appEndo' ('foldMap' ('Endo' . f) t ) z@--- [/foldr'/]--- @'foldr'' f z0 xs = let f\' k x z = k '$!' f x z in 'foldl' f\' 'id' xs z0@--- [/foldl/]--- @'foldl' f z t ≡ 'appEndo' ('getDual' ('foldMap' ('Dual' . 'Endo' . 'flip' f) t)) z@--- [/foldl'/]--- @'foldl'' f z0 xs = let f' x k z = k '$!' f z x in 'foldr' f\' 'id' xs z0@--- [/toList/]--- @'F.toList' ≡ 'foldr' (:) []@--- [/null/]--- @'null' ≡ 'foldr' ('const' ('const' 'False')) 'True'@--- [/length/]--- @'length' ≡ getSum . foldMap ('const' ('Sum' 1))@------ Note that this checks to ensure that @foldl\'@ and @foldr\'@--- are suitably strict.-foldableLaws :: (Foldable f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Laws-foldableLaws = foldableLawsInternal--foldableLawsInternal :: forall f. (Foldable f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Laws-foldableLawsInternal p = Laws "Foldable"- [ (,) "fold" $ property $ \(Apply (a :: f (Sum Integer))) ->- fold a == foldMap id a- , (,) "foldMap" $ property $ \(Apply (a :: f Integer)) (e :: Equation) ->- let f = Sum . runEquation e- in foldMap f a == foldr (mappend . f) mempty a- , (,) "foldr" $ property $ \(e :: EquationTwo) (z :: Integer) (Apply (t :: f Integer)) ->- let f = runEquationTwo e- in foldr f z t == appEndo (foldMap (Endo . f) t) z- , (,) "foldr'" (foldableFoldr' p)- , (,) "foldl" $ property $ \(e :: EquationTwo) (z :: Integer) (Apply (t :: f Integer)) ->- let f = runEquationTwo e- in foldl f z t == appEndo (getDual (foldMap (Dual . Endo . flip f) t)) z- , (,) "foldl'" (foldableFoldl' p)- , (,) "toList" $ property $ \(Apply (t :: f Integer)) ->- eq1 (F.toList t) (foldr (:) [] t)- , (,) "null" $ property $ \(Apply (t :: f Integer)) ->- null t == foldr (const (const False)) True t- , (,) "length" $ property $ \(Apply (t :: f Integer)) ->- length t == getSum (foldMap (const (Sum 1)) t)- ]--foldableFoldl' :: forall f. (Foldable f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Property-foldableFoldl' _ = property $ \(_ :: ChooseSecond) (_ :: LastNothing) (Apply (xs :: f (Bottom Integer))) ->- monadicIO $ do- let f :: Integer -> Bottom Integer -> Integer- f a b = case b of- BottomUndefined -> error "foldableFoldl' example"- BottomValue v -> if even v- then a- else v- z0 = 0- r1 <- lift $ do- let f' x k z = k $! f z x- e <- try (evaluate (foldr f' id xs z0))- case e of- Left (_ :: ErrorCall) -> return Nothing- Right i -> return (Just i)- r2 <- lift $ do- e <- try (evaluate (foldl' f z0 xs))- case e of- Left (_ :: ErrorCall) -> return Nothing- Right i -> return (Just i)- return (r1 == r2)--foldableFoldr' :: forall f. (Foldable f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Property-foldableFoldr' _ = property $ \(_ :: ChooseFirst) (_ :: LastNothing) (Apply (xs :: f (Bottom Integer))) ->- monadicIO $ do- let f :: Bottom Integer -> Integer -> Integer- f a b = case a of- BottomUndefined -> error "foldableFoldl' example"- BottomValue v -> if even v- then v- else b- z0 = 0- r1 <- lift $ do- let f' k x z = k $! f x z- e <- try (evaluate (foldl f' id xs z0))- case e of- Left (_ :: ErrorCall) -> return Nothing- Right i -> return (Just i)- r2 <- lift $ do- e <- try (evaluate (foldr' f z0 xs))- case e of- Left (_ :: ErrorCall) -> return Nothing- Right i -> return (Just i)- return (r1 == r2)--data ChooseSecond = ChooseSecond- deriving (Eq)--data ChooseFirst = ChooseFirst- deriving (Eq)--data LastNothing = LastNothing- deriving (Eq)--data Bottom a = BottomUndefined | BottomValue a- deriving (Eq)--instance Show ChooseFirst where- show ChooseFirst = "\\a b -> if even a then a else b"--instance Show ChooseSecond where- show ChooseSecond = "\\a b -> if even b then a else b"--instance Show LastNothing where- show LastNothing = "0"--instance Show a => Show (Bottom a) where- show x = case x of- BottomUndefined -> "undefined"- BottomValue a -> show a--instance Arbitrary ChooseSecond where- arbitrary = pure ChooseSecond--instance Arbitrary ChooseFirst where- arbitrary = pure ChooseFirst--instance Arbitrary LastNothing where- arbitrary = pure LastNothing--instance Arbitrary a => Arbitrary (Bottom a) where- arbitrary = fmap maybeToBottom arbitrary- shrink x = map maybeToBottom (shrink (bottomToMaybe x))--bottomToMaybe :: Bottom a -> Maybe a-bottomToMaybe BottomUndefined = Nothing-bottomToMaybe (BottomValue a) = Just a--maybeToBottom :: Maybe a -> Bottom a-maybeToBottom Nothing = BottomUndefined-maybeToBottom (Just a) = BottomValue a--data Apply f a = Apply { getApply :: f a }--instance (Eq1 f, Eq a) => Eq (Apply f a) where- Apply a == Apply b = eq1 a b--data LinearEquation = LinearEquation- { _linearEquationLinear :: Integer- , _linearEquationConstant :: Integer- } deriving (Eq)--data LinearEquationM m = LinearEquationM (m LinearEquation) (m LinearEquation)--runLinearEquation :: Integer -> LinearEquation -> Integer-runLinearEquation x (LinearEquation a b) = a * x + b--runLinearEquationM :: Functor m => LinearEquationM m -> Integer -> m Integer-runLinearEquationM (LinearEquationM e1 e2) i = if odd i- then fmap (runLinearEquation i) e1- else fmap (runLinearEquation i) e2--instance Eq1 m => Eq (LinearEquationM m) where- LinearEquationM a1 b1 == LinearEquationM a2 b2 = eq1 a1 a2 && eq1 b1 b2--showLinear :: Int -> LinearEquation -> ShowS-showLinear _ (LinearEquation a b) = shows a . showString " * x + " . shows b--showLinearList :: [LinearEquation] -> ShowS-showLinearList xs = appEndo $ mconcat- $ [Endo (showChar '[')]- ++ L.intersperse (Endo (showChar ',')) (map (Endo . showLinear 0) xs)- ++ [Endo (showChar ']')]--instance Show1 m => Show (LinearEquationM m) where- show (LinearEquationM a b) = (\f -> f "")- $ showString "\\x -> if odd x then "- . liftShowsPrec showLinear showLinearList 0 a- . showString " else "- . liftShowsPrec showLinear showLinearList 0 b--instance Arbitrary1 m => Arbitrary (LinearEquationM m) where- arbitrary = liftA2 LinearEquationM arbitrary1 arbitrary1- shrink (LinearEquationM a b) = concat- [ map (\x -> LinearEquationM x b) (shrink1 a)- , map (\x -> LinearEquationM a x) (shrink1 b)- ]--instance Arbitrary LinearEquation where- arbitrary = do- (a,b) <- arbitrary- return (LinearEquation (abs a) (abs b))- shrink (LinearEquation a b) =- let xs = shrink (a,b)- in map (\(x,y) -> LinearEquation (abs x) (abs y)) xs---- this is a quadratic equation-data Equation = Equation Integer Integer Integer- deriving (Eq)---- This show instance is does not actually provide a--- way to create an equation. Instead, it makes it look--- like a lambda.-instance Show Equation where- show (Equation a b c) = "\\x -> " ++ show a ++ " * x ^ 2 + " ++ show b ++ " * x + " ++ show c--instance Arbitrary Equation where- arbitrary = do- (a,b,c) <- arbitrary- return (Equation (abs a) (abs b) (abs c))- shrink (Equation a b c) =- let xs = shrink (a,b,c)- in map (\(x,y,z) -> Equation (abs x) (abs y) (abs z)) xs--runEquation :: Equation -> Integer -> Integer-runEquation (Equation a b c) x = a * x ^ (2 :: Integer) + b * x + c---- linear equation of two variables-data EquationTwo = EquationTwo Integer Integer- deriving (Eq)---- This show instance is does not actually provide a--- way to create an EquationTwo. Instead, it makes it look--- like a lambda that takes two variables.-instance Show EquationTwo where- show (EquationTwo a b) = "\\x y -> " ++ show a ++ " * x + " ++ show b ++ " * y"--instance Arbitrary EquationTwo where- arbitrary = do- (a,b) <- arbitrary- return (EquationTwo (abs a) (abs b))- shrink (EquationTwo a b) =- let xs = shrink (a,b)- in map (\(x,y) -> EquationTwo (abs x) (abs y)) xs--runEquationTwo :: EquationTwo -> Integer -> Integer -> Integer-runEquationTwo (EquationTwo a b) x y = a * x + b * y---- This show instance is intentionally a little bit wrong.--- We don't wrap the result in Apply since the end user--- should not be made aware of the Apply wrapper anyway.-instance (Show1 f, Show a) => Show (Apply f a) where- showsPrec p = showsPrec1 p . getApply--instance (Arbitrary1 f, Arbitrary a) => Arbitrary (Apply f a) where- arbitrary = fmap Apply arbitrary1- shrink = map Apply . shrink1 . getApply--functorIdentity :: forall f. (Functor f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Property-functorIdentity _ = property $ \(Apply (a :: f Integer)) -> eq1 (fmap id a) a--func1 :: Integer -> (Integer,Integer)-func1 i = (div (i + 5) 3, i * i - 2 * i + 1)--func2 :: (Integer,Integer) -> (Bool,Either Ordering Integer)-func2 (a,b) = (odd a, if even a then Left (compare a b) else Right (b + 2))--functorComposition :: forall f. (Functor f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Property-functorComposition _ = property $ \(Apply (a :: f Integer)) ->- eq1 (fmap func2 (fmap func1 a)) (fmap (func2 . func1) a)--functorConst :: forall f. (Functor f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Property-functorConst _ = property $ \(Apply (a :: f Integer)) ->- eq1 (fmap (const 'X') a) ('X' <$ a)--applicativeIdentity :: forall f. (Applicative f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Property-applicativeIdentity _ = property $ \(Apply (a :: f Integer)) -> eq1 (pure id <*> a) a--applicativeComposition :: forall f. (Applicative f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Property-applicativeComposition _ = property $ \(Apply (u' :: f Equation)) (Apply (v' :: f Equation)) (Apply (w :: f Integer)) ->- let u = fmap runEquation u'- v = fmap runEquation v'- in eq1 (pure (.) <*> u <*> v <*> w) (u <*> (v <*> w))--applicativeHomomorphism :: forall f. (Applicative f, Eq1 f, Show1 f) => Proxy f -> Property-applicativeHomomorphism _ = property $ \(e :: Equation) (a :: Integer) ->- let f = runEquation e- in eq1 (pure f <*> pure a) (pure (f a) :: f Integer)--applicativeInterchange :: forall f. (Applicative f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Property-applicativeInterchange _ = property $ \(Apply (u' :: f Equation)) (y :: Integer) ->- let u = fmap runEquation u'- in eq1 (u <*> pure y) (pure ($ y) <*> u)--applicativeLiftA2_1 :: forall f. (Applicative f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Property-applicativeLiftA2_1 _ = property $ \(Apply (f' :: f Equation)) (Apply (x :: f Integer)) -> - let f = fmap runEquation f'- in eq1 (liftA2 id f x) (f <*> x)--monadLeftIdentity :: forall f. (Monad f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Property-monadLeftIdentity _ = property $ \(k' :: LinearEquationM f) (a :: Integer) -> - let k = runLinearEquationM k'- in eq1 (return a >>= k) (k a)--monadRightIdentity :: forall f. (Monad f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Property-monadRightIdentity _ = property $ \(Apply (m :: f Integer)) -> - eq1 (m >>= return) m--monadAssociativity :: forall f. (Monad f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Property-monadAssociativity _ = property $ \(Apply (m :: f Integer)) (k' :: LinearEquationM f) (h' :: LinearEquationM f) -> - let k = runLinearEquationM k'- h = runLinearEquationM h'- in eq1 (m >>= (\x -> k x >>= h)) ((m >>= k) >>= h)--monadReturn :: forall f. (Monad f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Property-monadReturn _ = property $ \(x :: Integer) ->- eq1 (return x) (pure x :: f Integer)--monadAp :: forall f. (Monad f, Eq1 f, Show1 f, Arbitrary1 f) => Proxy f -> Property-monadAp _ = property $ \(Apply (f' :: f Equation)) (Apply (x :: f Integer)) -> - let f = fmap runEquation f'- in eq1 (ap f x) (f <*> x)+-- Ground Types+#if MIN_VERSION_base(4,7,0)+import Test.QuickCheck.Classes.IsList+#endif+#if HAVE_AESON+import Test.QuickCheck.Classes.Json+#endif+import Test.QuickCheck.Classes.Prim+#if HAVE_SEMIRINGS+import Test.QuickCheck.Classes.Euclidean+import Test.QuickCheck.Classes.Semiring+import Test.QuickCheck.Classes.Ring+#endif+-- Unary type constructors+#if HAVE_UNARY_LAWS+#if HAVE_SEMIGROUPOIDS+import Test.QuickCheck.Classes.Alt+import Test.QuickCheck.Classes.Apply+#endif+#if HAVE_SEMIGROUPOIDS+import Test.QuickCheck.Classes.Plus+#endif+#endif +-- Binary type constructors+#if HAVE_BINARY_LAWS+#if HAVE_SEMIGROUPOIDS+import Test.QuickCheck.Classes.Semigroupoid #endif+#endif -myForAllShrink :: (Arbitrary a, Show b, Eq b) => Bool -> (a -> [String]) -> String -> (a -> b) -> String -> (a -> b) -> Property-myForAllShrink displayRhs showInputs name1 calc1 name2 calc2 =- again $- MkProperty $- arbitrary >>= \x ->- unProperty $- shrinking shrink x $ \x' ->- let b1 = calc1 x'- b2 = calc2 x'- sb1 = show b1- sb2 = show b2- description = " Description: " ++ name1 ++ " = " ++ name2- err = description ++ "\n" ++ unlines (map (" " ++) (showInputs x)) ++ " " ++ name1 ++ " = " ++ sb1 ++ (if displayRhs then "\n " ++ name2 ++ " = " ++ sb2 else "")- in counterexample err (b1 == b2)+#if HAVE_VECTOR+import Test.QuickCheck.Classes.MVector+#endif +import qualified Test.QuickCheck.Classes.Base as QCB
+ src/Test/QuickCheck/Classes/Alt.hs view
@@ -0,0 +1,63 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE ScopedTypeVariables #-}++#if HAVE_QUANTIFIED_CONSTRAINTS+{-# LANGUAGE QuantifiedConstraints #-}+#endif++{-# OPTIONS_GHC -Wall #-}++module Test.QuickCheck.Classes.Alt+ (+#if defined(HAVE_SEMIGROUPOIDS) && defined(HAVE_UNARY_LAWS)+ altLaws+#endif+) where++#if defined(HAVE_SEMIGROUPOIDS) && defined(HAVE_UNARY_LAWS)+import Data.Functor+import Data.Functor.Alt (Alt)+import qualified Data.Functor.Alt as Alt+import Test.QuickCheck hiding ((.&.))+import Test.QuickCheck.Arbitrary (Arbitrary1(..))+import Data.Functor.Classes (Eq1,Show1)+import Test.QuickCheck.Property (Property)++import Test.QuickCheck.Classes.Internal++-- | Tests the following alt properties:+--+-- [/Associativity/]+-- @(a 'Alt.<!>' b) 'Alt.<!>' c ≡ a 'Alt.<!>' (b 'Alt.<!>' c)@+-- [/Left Distributivity/]+-- @f '<$>' (a 'Alt.<!>' b) ≡ (f '<$>' a) 'Alt.<!>' (f '<$>' b)@+altLaws :: forall proxy f.+#if HAVE_QUANTIFIED_CONSTRAINTS+ (Alt f, forall a. Eq a => Eq (f a), forall a. Show a => Show (f a), forall a. Arbitrary a => Arbitrary (f a))+#else+ (Alt f, Eq1 f, Show1 f, Arbitrary1 f)+#endif+ => proxy f -> Laws+altLaws p = Laws "Alt"+ [ ("Associativity", altAssociative p)+ , ("Left Distributivity", altLeftDistributive p)+ ]++altAssociative :: forall proxy f.+#if HAVE_QUANTIFIED_CONSTRAINTS+ (Alt f, forall a. Eq a => Eq (f a), forall a. Show a => Show (f a), forall a. Arbitrary a => Arbitrary (f a))+#else+ (Alt f, Eq1 f, Show1 f, Arbitrary1 f)+#endif+ => proxy f -> Property+altAssociative _ = property $ \(Apply (a :: f Integer)) (Apply (b :: f Integer)) (Apply (c :: f Integer)) -> eq1 ((a Alt.<!> b) Alt.<!> c) (a Alt.<!> (b Alt.<!> c))++altLeftDistributive :: forall proxy f.+#if HAVE_QUANTIFIED_CONSTRAINTS+ (Alt f, forall a. Eq a => Eq (f a), forall a. Show a => Show (f a), forall a. Arbitrary a => Arbitrary (f a))+#else+ (Alt f, Eq1 f, Show1 f, Arbitrary1 f)+#endif+ => proxy f -> Property+altLeftDistributive _ = property $ \(Apply (a :: f Integer)) (Apply (b :: f Integer)) -> eq1 (id <$> (a Alt.<!> b)) ((id <$> a) Alt.<!> (id <$> b))+#endif
+ src/Test/QuickCheck/Classes/Apply.hs view
@@ -0,0 +1,65 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}++#if HAVE_QUANTIFIED_CONSTRAINTS+{-# LANGUAGE QuantifiedConstraints #-}+#endif++{-# OPTIONS_GHC -Wall #-}++module Test.QuickCheck.Classes.Apply+ (+#if defined(HAVE_SEMIGROUPOIDS) && defined(HAVE_UNARY_LAWS)+ applyLaws+#endif+) where++#if defined(HAVE_SEMIGROUPOIDS) && defined(HAVE_UNARY_LAWS)+import Data.Functor+import qualified Data.Functor.Apply as FunctorApply+import Test.QuickCheck hiding ((.&.))+import Test.QuickCheck.Arbitrary (Arbitrary1(..))+import Data.Functor.Classes (Eq1,Show1)+import Test.QuickCheck.Property (Property)++import Test.QuickCheck.Classes.Internal++type ApplyProp proxy f =+#if HAVE_QUANTIFIED_CONSTRAINTS+ (FunctorApply.Apply f, forall x. Eq x => Eq (f x), forall x. Show x => Show (f x), forall x. Arbitrary x => Arbitrary (f x)) +#else+ (FunctorApply.Apply f, Eq1 f, Show1 f, Arbitrary1 f)+#endif+ => proxy f -> Property++-- | Tests the following alt properties:+--+-- [/LiftF2 (1)/]+-- @('FunctorApply.<.>') ≡ 'FunctorApply.liftF2' 'id'@+-- [/Associativity/]+-- @'fmap' ('.') u 'FunctorApply.<.>' v 'FunctorApply.<.>' w ≡ u 'FunctorApply.<.>' (v 'FunctorApply.<.>' w)@+applyLaws ::+#if HAVE_QUANTIFIED_CONSTRAINTS+ (FunctorApply.Apply f, forall a. Eq a => Eq (f a), forall a. Show a => Show (f a), forall a. Arbitrary a => Arbitrary (f a))+#else+ (FunctorApply.Apply f, Eq1 f, Show1 f, Arbitrary1 f)+#endif+ => proxy f -> Laws+applyLaws p = Laws "Apply"+ [ ("LiftF2 part 1", applyLiftF2_1 p)+ , ("Associativity", applyAssociativity p)+ ]++applyLiftF2_1 :: forall proxy f. ApplyProp proxy f+applyLiftF2_1 _ = property $ \(Apply (f' :: f QuadraticEquation)) (Apply (x :: f Integer)) ->+ let f = fmap runQuadraticEquation f'+ in eq1 (FunctorApply.liftF2 id f x) (f FunctorApply.<.> x)++applyAssociativity :: forall proxy f. ApplyProp proxy f+applyAssociativity _ = property $ \(Apply (u' :: f QuadraticEquation)) (Apply (v' :: f QuadraticEquation)) (Apply (w :: f Integer)) ->+ let u = fmap runQuadraticEquation u'+ v = fmap runQuadraticEquation v'+ in eq1 (fmap (.) u FunctorApply.<.> v FunctorApply.<.> w) (u FunctorApply.<.> (v FunctorApply.<.> w))++#endif
+ src/Test/QuickCheck/Classes/Euclidean.hs view
@@ -0,0 +1,122 @@+-- |+-- Module: Test.QuickCheck.Classes.Euclidean+-- Copyright: (c) 2019 Andrew Lelechenko+-- Licence: BSD3+--++{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}++{-# OPTIONS_GHC -Wall #-}++#if !HAVE_SEMIRINGS+module Test.QuickCheck.Classes.Euclidean where+#else++module Test.QuickCheck.Classes.Euclidean+ ( gcdDomainLaws+ , euclideanLaws+ ) where++import Prelude hiding (quotRem, quot, rem, gcd, lcm)+import Data.Maybe+import Data.Proxy (Proxy)+import Data.Euclidean+import Data.Semiring (Semiring(..))++import Test.QuickCheck hiding ((.&.))+import Test.QuickCheck.Property (Property)++import Test.QuickCheck.Classes.Internal (Laws(..))++-- | Test that a 'GcdDomain' instance obey several laws.+--+-- Check that 'divide' is an inverse of times:+--+-- * @y \/= 0 => (x * y) \`divide\` y == Just x@,+-- * @y \/= 0, x \`divide\` y == Just z => x == z * y@.+--+-- Check that 'gcd' is a common divisor and is a multiple of any common divisor:+--+-- * @x \/= 0, y \/= 0 => isJust (x \`divide\` gcd x y) && isJust (y \`divide\` gcd x y)@,+-- * @z \/= 0 => isJust (gcd (x * z) (y * z) \`divide\` z)@.+--+-- Check that 'lcm' is a common multiple and is a factor of any common multiple:+--+-- * @x \/= 0, y \/= 0 => isJust (lcm x y \`divide\` x) && isJust (lcm x y \`divide\` y)@,+-- * @x \/= 0, y \/= 0, isJust (z \`divide\` x), isJust (z \`divide\` y) => isJust (z \`divide\` lcm x y)@.+--+-- Check that 'gcd' of 'coprime' numbers is a unit of the semiring (has an inverse):+--+-- * @y \/= 0, coprime x y => isJust (1 \`divide\` gcd x y)@.+gcdDomainLaws :: (Eq a, GcdDomain a, Arbitrary a, Show a) => Proxy a -> Laws+gcdDomainLaws p = Laws "GcdDomain"+ [ ("divide1", divideLaw1 p)+ , ("divide2", divideLaw2 p)+ , ("gcd1", gcdLaw1 p)+ , ("gcd2", gcdLaw2 p)+ , ("lcm1", lcmLaw1 p)+ , ("lcm2", lcmLaw2 p)+ , ("coprime", coprimeLaw p)+ ]++divideLaw1 :: forall a. (Eq a, GcdDomain a, Arbitrary a, Show a) => Proxy a -> Property+divideLaw1 _ = property $ \(x :: a) y ->+ y /= zero ==> (x `times` y) `divide` y === Just x++divideLaw2 :: forall a. (Eq a, GcdDomain a, Arbitrary a, Show a) => Proxy a -> Property+divideLaw2 _ = property $ \(x :: a) y ->+ y /= zero ==> maybe (property True) (\z -> x === z `times` y) (x `divide` y)++gcdLaw1 :: forall a. (Eq a, GcdDomain a, Arbitrary a, Show a) => Proxy a -> Property+gcdLaw1 _ = property $ \(x :: a) y ->+ x /= zero || y /= zero ==> isJust (x `divide` gcd x y) .&&. isJust (y `divide` gcd x y)++gcdLaw2 :: forall a. (Eq a, GcdDomain a, Arbitrary a, Show a) => Proxy a -> Property+gcdLaw2 _ = property $ \(x :: a) y z ->+ z /= zero ==> isJust (gcd (x `times` z) (y `times` z) `divide` z)++lcmLaw1 :: forall a. (Eq a, GcdDomain a, Arbitrary a, Show a) => Proxy a -> Property+lcmLaw1 _ = property $ \(x :: a) y ->+ x /= zero && y /= zero ==> isJust (lcm x y `divide` x) .&&. isJust (lcm x y `divide` y)++lcmLaw2 :: forall a. (Eq a, GcdDomain a, Arbitrary a, Show a) => Proxy a -> Property+lcmLaw2 _ = property $ \(x :: a) y z ->+ x /= zero && y /= zero ==> isNothing (z `divide` x) .||. isNothing (z `divide` y) .||. isJust (z `divide` lcm x y)++coprimeLaw :: forall a. (Eq a, GcdDomain a, Arbitrary a, Show a) => Proxy a -> Property+coprimeLaw _ = property $ \(x :: a) y ->+ y /= zero ==> coprime x y === isJust (one `divide` gcd x y)++-- | Test that a 'Euclidean' instance obey laws of a Euclidean domain.+--+-- * @y \/= 0, r == x \`rem\` y => r == 0 || degree r < degree y@,+-- * @y \/= 0, (q, r) == x \`quotRem\` y => x == q * y + r@,+-- * @y \/= 0 => x \`quot\` x y == fst (x \`quotRem\` y)@,+-- * @y \/= 0 => x \`rem\` x y == snd (x \`quotRem\` y)@.+euclideanLaws :: (Eq a, Euclidean a, Arbitrary a, Show a) => Proxy a -> Laws+euclideanLaws p = Laws "Euclidean"+ [ ("degree", degreeLaw p)+ , ("quotRem", quotRemLaw p)+ , ("quot", quotLaw p)+ , ("rem", remLaw p)+ ]++degreeLaw :: forall a. (Eq a, Euclidean a, Arbitrary a, Show a) => Proxy a -> Property+degreeLaw _ = property $ \(x :: a) y ->+ y /= zero ==> let (_, r) = x `quotRem` y in (r === zero .||. degree r < degree y)++quotRemLaw :: forall a. (Eq a, Euclidean a, Arbitrary a, Show a) => Proxy a -> Property+quotRemLaw _ = property $ \(x :: a) y ->+ y /= zero ==> let (q, r) = x `quotRem` y in x === (q `times` y) `plus` r++quotLaw :: forall a. (Eq a, Euclidean a, Arbitrary a, Show a) => Proxy a -> Property+quotLaw _ = property $ \(x :: a) y ->+ y /= zero ==> quot x y === fst (quotRem x y)++remLaw :: forall a. (Eq a, Euclidean a, Arbitrary a, Show a) => Proxy a -> Property+remLaw _ = property $ \(x :: a) y ->+ y /= zero ==> rem x y === snd (quotRem x y)++#endif
+ src/Test/QuickCheck/Classes/IsList.hs view
@@ -0,0 +1,8 @@+module Test.QuickCheck.Classes.IsList+ ( module Test.QuickCheck.Classes.Base.IsList+ ) where++-- It would be better to do this with Cabal's module reexport feature,+-- but that would break compatibility with older GHCs.++import Test.QuickCheck.Classes.Base.IsList
+ src/Test/QuickCheck/Classes/Json.hs view
@@ -0,0 +1,68 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE ScopedTypeVariables #-}++{-# OPTIONS_GHC -Wall #-}++module Test.QuickCheck.Classes.Json+ (+#if HAVE_AESON+ jsonLaws+#endif + ) where++import Data.Proxy (Proxy)+import Test.QuickCheck hiding ((.&.))+import Test.QuickCheck.Property (Property(..))++#if HAVE_AESON+import Data.Aeson (FromJSON(..), ToJSON(..))+import qualified Data.Aeson as AE+#endif++import Test.QuickCheck.Classes.Internal (Laws(..))++-- | Tests the following properties:+--+-- [/Partial Isomorphism/]+-- @decode . encode ≡ Just@+-- [/Encoding Equals Value/]+-- @decode . encode ≡ Just . toJSON@+--+-- Note that in the second property, the type of decode is @ByteString -> Value@,+-- not @ByteString -> a@+#if HAVE_AESON+jsonLaws :: (ToJSON a, FromJSON a, Show a, Arbitrary a, Eq a) => Proxy a -> Laws+jsonLaws p = Laws "ToJSON/FromJSON"+ [ ("Partial Isomorphism", jsonEncodingPartialIsomorphism p)+ , ("Encoding Equals Value", jsonEncodingEqualsValue p)+ ]++-- TODO: improve the quality of the error message if+-- something does not pass this test.+jsonEncodingEqualsValue :: forall a. (ToJSON a, Show a, Arbitrary a) => Proxy a -> Property+jsonEncodingEqualsValue _ = property $ \(a :: a) ->+ case AE.decode (AE.encode a) of+ Nothing -> False+ Just (v :: AE.Value) -> v == toJSON a++jsonEncodingPartialIsomorphism :: forall a. (ToJSON a, FromJSON a, Show a, Eq a, Arbitrary a) => Proxy a -> Property+jsonEncodingPartialIsomorphism _ =+#if MIN_VERSION_QuickCheck(2,9,0)+ again $+#endif+ MkProperty $+ arbitrary >>= \(x :: a) ->+ unProperty $+ shrinking shrink x $ \x' ->+ let desc1 = "Just"+ desc2 = "Data.Aeson.decode . Data.Aeson.encode"+ name1 = "Data.Aeson.encode a"+ name2 = "Data.Aeson.decode (Data.Aeson.encode a)"+ b1 = AE.encode x'+ b2 = AE.decode (AE.encode x')+ sb1 = show b1+ sb2 = show b2+ description = " Description: " ++ desc1 ++ " == " ++ desc2+ err = description ++ "\n" ++ unlines (map (" " ++) (["a = " ++ show x'])) ++ " " ++ name1 ++ " = " ++ sb1 ++ "\n " ++ name2 ++ " = " ++ sb2+ in counterexample err (Just x' == b2)+#endif
+ src/Test/QuickCheck/Classes/MVector.hs view
@@ -0,0 +1,354 @@+-- |+-- Module: Test.QuickCheck.Classes.MVector+-- Copyright: (c) 2019 Andrew Lelechenko+-- Licence: BSD3+--++{-# LANGUAGE CPP #-}+{-# LANGUAGE ScopedTypeVariables #-}++{-# OPTIONS_GHC -Wall #-}++#if !HAVE_VECTOR+module Test.QuickCheck.Classes.MVector where+#else++module Test.QuickCheck.Classes.MVector+ ( muvectorLaws+ ) where++import Control.Applicative+import Control.Monad (when)+import Control.Monad.ST+import Data.Functor+import Data.Proxy (Proxy)+import qualified Data.Vector.Generic.Mutable as MU (basicInitialize)+import qualified Data.Vector.Unboxed.Mutable as MU++import Test.QuickCheck hiding ((.&.))+import Test.QuickCheck.Property (Property)++import Test.QuickCheck.Classes.Internal (Laws(..))++-- | Test that a 'Vector.Unboxed.MVector' instance obey several laws.+muvectorLaws :: (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Laws+muvectorLaws p = Laws "Vector.Unboxed.MVector"+ [ ("New-Length", newLength p)+ , ("Replicate-Length", replicateLength p)+ , ("Slice-Length", sliceLength p)+ , ("Grow-Length", growLength p)++ , ("Write-Read", writeRead p)+ , ("Set-Read", setRead p)+ , ("Sliced-Set-Read", slicedSetRead p)+ , ("Replicate-Read", replicateRead p)++ , ("Slice-Overlaps", sliceOverlaps p)+ , ("Slice-Copy", sliceCopy p)+ , ("Slice-Move", sliceMove p)++ , ("Write-Copy-Read", writeCopyRead p)+ , ("Write-Move-Read", writeMoveRead p)+ , ("Write-Grow-Read", writeGrowRead p)+ , ("Sliced-Write-Copy-Read", slicedWriteCopyRead p)+ , ("Sliced-Write-Move-Read", slicedWriteMoveRead p)+ , ("Sliced-Write-Grow-Read", slicedWriteGrowRead p)++ , ("Write-InitializeAround-Read", writeInitializeAroundRead p)+ , ("Write-ClearAround-Read", writeClearAroundRead p)+ , ("Write-SetAround-Read", writeSetAroundRead p)+ , ("Write-WriteAround-Read", writeWriteAroundRead p)+ , ("Write-CopyAround-Read", writeCopyAroundRead p)+ , ("Write-MoveAround-Read", writeMoveAroundRead p)++ , ("Write-InitializeBetween-Read", writeInitializeBetweenRead p)+ , ("Write-ClearBetween-Read", writeClearBetweenRead p)+ , ("Write-SetBetween-Read", writeSetBetweenRead p)+ , ("Write-CopyBetween-Read", writeCopyBetweenRead p)+ , ("Write-MoveBetween-Read", writeMoveBetweenRead p)+ ]++-------------------------------------------------------------------------------+-- Length++newLength :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property+newLength _ = property $ \(NonNegative len) -> do+ (=== len) (runST $ MU.length <$> (MU.new len :: ST s (MU.MVector s a)))++replicateLength :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property+replicateLength _ = property $ \(a :: a) (NonNegative len) -> do+ (=== len) (runST $ MU.length <$> MU.replicate len a)++sliceLength :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property+sliceLength _ = property $ \(NonNegative ix) (NonNegative subLen) (Positive excess) -> do+ (=== subLen) (runST $ MU.length . MU.slice ix subLen <$> (MU.new (ix + subLen + excess) :: ST s (MU.MVector s a)))++growLength :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property+growLength _ = property $ \(Positive len) (Positive by) -> do+ (=== len + by) $ runST $ do+ arr <- MU.new len :: ST s (MU.MVector s a)+ MU.length <$> MU.grow arr by++-------------------------------------------------------------------------------+-- Read++writeRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property+writeRead _ = property $ \(a :: a) (NonNegative ix) (Positive excess) -> do+ (=== a) $ runST $ do+ arr <- MU.new (ix + excess)+ MU.write arr ix a+ MU.read arr ix++setRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property+setRead _ = property $ \(a :: a) (NonNegative ix) (Positive excess) -> do+ (=== a) $ runST $ do+ arr <- MU.new (ix + excess)+ MU.set arr a+ MU.read arr ix++slicedSetRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property+slicedSetRead _ = property $ \(a :: a) (NonNegative ix) (Positive excess) before after -> do+ (=== a) $ runST $ do+ arr <- newSlice before after (ix + excess)+ MU.set arr a+ MU.read arr ix++replicateRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property+replicateRead _ = property $ \(a :: a) (NonNegative ix) (Positive excess) -> do+ (=== a) $ runST $ do+ arr <- MU.replicate (ix + excess) a+ MU.read arr ix++-------------------------------------------------------------------------------+-- Overlaps++sliceOverlaps :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property+sliceOverlaps _ = property $ \(NonNegative i) (NonNegative ij) (NonNegative jk) (NonNegative kl) (NonNegative lm) -> do+ let j = i + ij+ k = j + jk+ l = k + kl+ m = l + lm+ property $ runST $ do+ arr <- MU.new (m + 1) :: ST s (MU.MVector s a)+ let slice1 = MU.slice i (k - i + 1) arr+ slice2 = MU.slice j (l - j + 1) arr+ pure $ MU.overlaps slice1 slice2++sliceCopy :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property+sliceCopy _ = property $ \(a :: a) (NonNegative i) (NonNegative ix) (Positive excess) (NonNegative ij) (NonNegative jk) -> do+ let j = i + ix + excess + ij+ k = j + ix + excess + jk+ runST $ do+ arr <- MU.new k :: ST s (MU.MVector s a)+ let src = MU.slice i (ix + excess) arr+ dst = MU.slice j (ix + excess) arr+ if MU.overlaps src dst then pure (property True) else do+ MU.write src ix a+ MU.copy dst src+ valSrc <- MU.read src ix+ valDst <- MU.read dst ix+ pure (valSrc === a .&&. valDst === a)++sliceMove :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property+sliceMove _ = property $ \(a :: a) (NonNegative i) (NonNegative ix) (Positive excess) (NonNegative ij) (NonNegative jk) -> do+ let j = i + ix + excess + ij+ k = j + ix + excess + jk+ (=== a) $ runST $ do+ arr <- MU.new k :: ST s (MU.MVector s a)+ let src = MU.slice i (ix + excess) arr+ dst = MU.slice j (ix + excess) arr+ MU.write src ix a+ MU.move dst src+ MU.read dst ix++-------------------------------------------------------------------------------+-- Write + copy/move/grow++writeCopyRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property+writeCopyRead _ = property $ \(a :: a) (NonNegative ix) (Positive excess) -> do+ (=== a) $ runST $ do+ src <- MU.new (ix + excess)+ MU.write src ix a+ dst <- MU.new (ix + excess)+ MU.copy dst src+ MU.clear src+ MU.read dst ix++writeMoveRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property+writeMoveRead _ = property $ \(a :: a) (NonNegative ix) (Positive excess) -> do+ (=== a) $ runST $ do+ src <- MU.new (ix + excess)+ MU.write src ix a+ dst <- MU.new (ix + excess)+ MU.move dst src+ MU.clear src+ MU.read dst ix++writeGrowRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property+writeGrowRead _ = property $ \(a :: a) (NonNegative ix) (Positive excess) (Positive by) -> do+ (=== a) $ runST $ do+ src <- MU.new (ix + excess)+ MU.write src ix a+ dst <- MU.grow src by+ MU.clear src+ MU.read dst ix++slicedWriteCopyRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property+slicedWriteCopyRead _ = property $ \(a :: a) (NonNegative ix) (Positive excess) beforeSrc afterSrc beforeDst afterDst -> do+ (=== a) $ runST $ do+ src <- newSlice beforeSrc afterSrc (ix + excess)+ MU.write src ix a+ dst <- newSlice beforeDst afterDst (ix + excess)+ MU.copy dst src+ MU.clear src+ MU.read dst ix++slicedWriteMoveRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property+slicedWriteMoveRead _ = property $ \(a :: a) (NonNegative ix) (Positive excess) beforeSrc afterSrc beforeDst afterDst -> do+ (=== a) $ runST $ do+ src <- newSlice beforeSrc afterSrc (ix + excess)+ MU.write src ix a+ dst <- newSlice beforeDst afterDst (ix + excess)+ MU.move dst src+ MU.clear src+ MU.read dst ix++slicedWriteGrowRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property+slicedWriteGrowRead _ = property $ \(a :: a) (NonNegative ix) (Positive excess) (Positive by) beforeSrc afterSrc -> do+ (=== a) $ runST $ do+ src <- newSlice beforeSrc afterSrc (ix + excess)+ MU.write src ix a+ dst <- MU.grow src by+ MU.clear src+ MU.read dst ix++-------------------------------------------------------------------------------+-- Write + overwrite around++writeInitializeAroundRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property+writeInitializeAroundRead _ = property $ \(a :: a) (NonNegative ix) (Positive excess) -> do+ (=== a) $ runST $ do+ arr <- MU.new (ix + excess)+ MU.write arr ix a+ when (ix > 0) $+ MU.basicInitialize (MU.slice 0 ix arr)+ when (excess > 1) $+ MU.basicInitialize (MU.slice (ix + 1) (excess - 1) arr)+ MU.read arr ix++writeClearAroundRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property+writeClearAroundRead _ = property $ \(a :: a) (NonNegative ix) (Positive excess) -> do+ (=== a) $ runST $ do+ arr <- MU.new (ix + excess)+ MU.write arr ix a+ when (ix > 0) $+ MU.clear (MU.slice 0 ix arr)+ when (excess > 1) $+ MU.clear (MU.slice (ix + 1) (excess - 1) arr)+ MU.read arr ix++writeSetAroundRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property+writeSetAroundRead _ = property $ \(a :: a) (b :: a) (NonNegative ix) (Positive excess) -> do+ (=== a) $ runST $ do+ arr <- MU.new (ix + excess)+ MU.write arr ix a+ when (ix > 0) $+ MU.set (MU.slice 0 ix arr) b+ when (excess > 1) $+ MU.set (MU.slice (ix + 1) (excess - 1) arr) b+ MU.read arr ix++writeWriteAroundRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property+writeWriteAroundRead _ = property $ \(a :: a) (b :: a) (NonNegative ix) (Positive excess) -> do+ (=== a) $ runST $ do+ arr <- MU.new (ix + excess)+ MU.write arr ix a+ when (ix > 0) $+ MU.write arr (ix - 1) b+ when (excess > 1) $+ MU.write arr (ix + 1) b+ MU.read arr ix++writeCopyAroundRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property+writeCopyAroundRead _ = property $ \(a :: a) (NonNegative ix) (Positive excess) -> do+ (=== a) $ runST $ do+ src <- MU.new (ix + excess)+ dst <- MU.new (ix + excess)+ MU.write dst ix a+ when (ix > 0) $+ MU.copy (MU.slice 0 ix dst) (MU.slice 0 ix src)+ when (excess > 1) $+ MU.copy (MU.slice (ix + 1) (excess - 1) dst) (MU.slice (ix + 1) (excess - 1) src)+ MU.read dst ix++writeMoveAroundRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property+writeMoveAroundRead _ = property $ \(a :: a) (NonNegative ix) (Positive excess) -> do+ (=== a) $ runST $ do+ src <- MU.new (ix + excess)+ dst <- MU.new (ix + excess)+ MU.write dst ix a+ when (ix > 0) $+ MU.move (MU.slice 0 ix dst) (MU.slice 0 ix src)+ when (excess > 1) $+ MU.move (MU.slice (ix + 1) (excess - 1) dst) (MU.slice (ix + 1) (excess - 1) src)+ MU.read dst ix++-------------------------------------------------------------------------------+-- Two writes + overwrite in between++writeInitializeBetweenRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property+writeInitializeBetweenRead _ = property $ \(a :: a) (b :: a) (NonNegative ix) (Positive dix) (Positive excess) -> do+ (=== (a, b)) $ runST $ do+ arr <- MU.new (ix + dix + excess)+ MU.write arr ix a+ MU.write arr (ix + dix) b+ MU.basicInitialize (MU.slice (ix + 1) (dix - 1) arr)+ (,) <$> MU.read arr ix <*> MU.read arr (ix + dix)++writeClearBetweenRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property+writeClearBetweenRead _ = property $ \(a :: a) (b :: a) (NonNegative ix) (Positive dix) (Positive excess) -> do+ (=== (a, b)) $ runST $ do+ arr <- MU.new (ix + dix + excess)+ MU.write arr ix a+ MU.write arr (ix + dix) b+ MU.clear (MU.slice (ix + 1) (dix - 1) arr)+ (,) <$> MU.read arr ix <*> MU.read arr (ix + dix)++writeSetBetweenRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property+writeSetBetweenRead _ = property $ \(a :: a) (b :: a) (c :: a) (NonNegative ix) (Positive dix) (Positive excess) -> do+ (=== (a, b)) $ runST $ do+ arr <- MU.new (ix + dix + excess)+ MU.write arr ix a+ MU.write arr (ix + dix) b+ MU.set (MU.slice (ix + 1) (dix - 1) arr) c+ (,) <$> MU.read arr ix <*> MU.read arr (ix + dix)++writeCopyBetweenRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property+writeCopyBetweenRead _ = property $ \(a :: a) (b :: a) (NonNegative ix) (Positive dix) (Positive excess) -> do+ (=== (a, b)) $ runST $ do+ src <- MU.new (ix + dix + excess)+ dst <- MU.new (ix + dix + excess)+ MU.write dst ix a+ MU.write dst (ix + dix) b+ MU.copy (MU.slice (ix + 1) (dix - 1) dst) (MU.slice (ix + 1) (dix - 1) src)+ (,) <$> MU.read dst ix <*> MU.read dst (ix + dix)++writeMoveBetweenRead :: forall a. (Eq a, MU.Unbox a, Arbitrary a, Show a) => Proxy a -> Property+writeMoveBetweenRead _ = property $ \(a :: a) (b :: a) (NonNegative ix) (Positive dix) (Positive excess) -> do+ (=== (a, b)) $ runST $ do+ src <- MU.new (ix + dix + excess)+ dst <- MU.new (ix + dix + excess)+ MU.write dst ix a+ MU.write dst (ix + dix) b+ MU.move (MU.slice (ix + 1) (dix - 1) dst) (MU.slice (ix + 1) (dix - 1) src)+ (,) <$> MU.read dst ix <*> MU.read dst (ix + dix)++-------------------------------------------------------------------------------+-- Utils++newSlice :: MU.Unbox a => NonNegative Int -> NonNegative Int -> Int -> ST s (MU.MVector s a)+newSlice (NonNegative before) (NonNegative after) len = do+ arr <- MU.new (before + len + after)+ pure $ MU.slice before len arr++#endif
+ src/Test/QuickCheck/Classes/Plus.hs view
@@ -0,0 +1,93 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE ScopedTypeVariables #-}++#if HAVE_QUANTIFIED_CONSTRAINTS+{-# LANGUAGE QuantifiedConstraints #-}+#endif++{-# OPTIONS_GHC -Wall #-}++module Test.QuickCheck.Classes.Plus+ (+#if defined(HAVE_SEMIGROUPOIDS) && defined(HAVE_UNARY_LAWS)+ plusLaws+ , extendedPlusLaws+#endif+ ) where++#if defined(HAVE_SEMIGROUPOIDS) && defined(HAVE_UNARY_LAWS)+import Data.Functor+import Data.Functor.Alt (Alt)+import Data.Functor.Plus (Plus)+import qualified Data.Functor.Alt as Alt+import qualified Data.Functor.Plus as Plus++import Test.QuickCheck hiding ((.&.))+import Test.QuickCheck.Arbitrary (Arbitrary1(..))+import Data.Functor.Classes (Eq1,Show1)+import qualified Control.Applicative as Alternative+import Test.QuickCheck.Property (Property)++import Test.QuickCheck.Classes.Internal++-- | Tests the following alt properties:+--+-- [/Left Identity/]+-- @'Plus.zero' 'Alt.<!>' m ≡ m@+-- [/Right Identity/]+-- @m 'Alt.<!>' 'Plus.zero' ≡ m@+plusLaws :: forall proxy f.+#if HAVE_QUANTIFIED_CONSTRAINTS+ (Plus f, forall a. Eq a => Eq (f a), forall a. Show a => Show (f a), forall a. Arbitrary a => Arbitrary (f a))+#else+ (Plus f, Eq1 f, Show1 f, Arbitrary1 f)+#endif+ => proxy f -> Laws+plusLaws p = Laws "Plus"+ [ ("Left Identity", plusLeftIdentity p)+ , ("Right Identity", plusRightIdentity p)+ ]++-- | Tests everything from 'altLaws', plus the following:+--+-- [/Congruency/]+-- @'Plus.zero' ≡ 'Alternative.empty'@+extendedPlusLaws :: forall proxy f.+#if HAVE_QUANTIFIED_CONSTRAINTS+ (Plus f, Alternative.Alternative f, forall a. Eq a => Eq (f a), forall a. Show a => Show (f a), forall a. Arbitrary a => Arbitrary (f a))+#else+ (Plus f, Alternative.Alternative f, Eq1 f, Show1 f, Arbitrary1 f)+#endif+ => proxy f -> Laws+extendedPlusLaws p = Laws "Plus extended to Alternative" $ lawsProperties (plusLaws p) +++ [ ("Congruency", extendedPlusLaw p)+ ]++extendedPlusLaw :: forall proxy f.+#if HAVE_QUANTIFIED_CONSTRAINTS+ (Plus f, Alternative.Alternative f, forall a. Eq a => Eq (f a), forall a. Show a => Show (f a), forall a. Arbitrary a => Arbitrary (f a))+#else+ (Plus f, Alternative.Alternative f, Eq1 f, Show1 f, Arbitrary1 f)+#endif+ => proxy f -> Property+extendedPlusLaw _ = property $ eq1 (Plus.zero :: f Integer) (Alternative.empty :: f Integer)++plusLeftIdentity :: forall proxy f.+#if HAVE_QUANTIFIED_CONSTRAINTS+ (Plus f, forall a. Eq a => Eq (f a), forall a. Show a => Show (f a), forall a. Arbitrary a => Arbitrary (f a))+#else+ (Plus f, Eq1 f, Show1 f, Arbitrary1 f)+#endif+ => proxy f -> Property+plusLeftIdentity _ = property $ \(Apply (m :: f Integer)) -> eq1 (Plus.zero Alt.<!> m) m++plusRightIdentity :: forall proxy f.+#if HAVE_QUANTIFIED_CONSTRAINTS+ (Plus f, forall a. Eq a => Eq (f a), forall a. Show a => Show (f a), forall a. Arbitrary a => Arbitrary (f a))+#else+ (Plus f, Eq1 f, Show1 f, Arbitrary1 f)+#endif+ => proxy f -> Property+plusRightIdentity _ = property $ \(Apply (m :: f Integer)) -> eq1 (m Alt.<!> Plus.zero) m++#endif
+ src/Test/QuickCheck/Classes/Prim.hs view
@@ -0,0 +1,390 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE PackageImports #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UnboxedTuples #-}++{-# OPTIONS_GHC -Wall #-}++module Test.QuickCheck.Classes.Prim+ ( primLaws+ ) where++import Control.Applicative+import Control.Monad.Primitive (PrimMonad, PrimState,primitive,primitive_)+import Control.Monad.ST+import Data.Proxy (Proxy)+import Data.Primitive.ByteArray+import Data.Primitive.Types (Prim(..))+import "primitive-addr" Data.Primitive.Addr+import Foreign.Marshal.Alloc+import GHC.Exts+ (State#,Int#,Addr#,Int(I#),(*#),(+#),(<#),newByteArray#,unsafeFreezeByteArray#,+ copyMutableByteArray#,copyByteArray#,quotInt#,sizeofByteArray#)++#if MIN_VERSION_base(4,7,0)+import GHC.Exts (IsList(fromList,toList,fromListN),Item,+ copyByteArrayToAddr#,copyAddrToByteArray#)+#endif++import GHC.Ptr (Ptr(..))+import System.IO.Unsafe+import Test.QuickCheck hiding ((.&.))+import Test.QuickCheck.Property (Property)++import qualified Data.List as L+import qualified Data.Primitive as P++import Test.QuickCheck.Classes.Internal (Laws(..),isTrue#)++-- | Test that a 'Prim' instance obey the several laws.+primLaws :: (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Laws+primLaws p = Laws "Prim"+ [ ("ByteArray Put-Get (you get back what you put in)", primPutGetByteArray p)+ , ("ByteArray Get-Put (putting back what you got out has no effect)", primGetPutByteArray p)+ , ("ByteArray Put-Put (putting twice is same as putting once)", primPutPutByteArray p)+ , ("ByteArray Set Range", primSetByteArray p)+#if MIN_VERSION_base(4,7,0)+ , ("ByteArray List Conversion Roundtrips", primListByteArray p)+#endif+ , ("Addr Put-Get (you get back what you put in)", primPutGetAddr p)+ , ("Addr Get-Put (putting back what you got out has no effect)", primGetPutAddr p)+ , ("Addr Set Range", primSetOffAddr p)+ , ("Addr List Conversion Roundtrips", primListAddr p)+ ]++primListAddr :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property+primListAddr _ = property $ \(as :: [a]) -> unsafePerformIO $ do+ let len = L.length as+ ptr@(Ptr addr#) :: Ptr a <- mallocBytes (len * P.sizeOf (undefined :: a))+ let addr = Addr addr#+ let go :: Int -> [a] -> IO ()+ go !ix xs = case xs of+ [] -> return ()+ (x : xsNext) -> do+ writeOffAddr addr ix x+ go (ix + 1) xsNext+ go 0 as+ let rebuild :: Int -> IO [a]+ rebuild !ix = if ix < len+ then (:) <$> readOffAddr addr ix <*> rebuild (ix + 1)+ else return []+ asNew <- rebuild 0+ free ptr+ return (as == asNew)++primPutGetByteArray :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property+primPutGetByteArray _ = property $ \(a :: a) len -> (len > 0) ==> do+ ix <- choose (0,len - 1)+ return $ runST $ do+ arr <- newPrimArray len+ writePrimArray arr ix a+ a' <- readPrimArray arr ix+ return (a == a')++primGetPutByteArray :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property+primGetPutByteArray _ = property $ \(as :: [a]) -> (not (L.null as)) ==> do+ let arr1 = primArrayFromList as :: PrimArray a+ len = L.length as+ ix <- choose (0,len - 1)+ arr2 <- return $ runST $ do+ marr <- newPrimArray len+ copyPrimArray marr 0 arr1 0 len+ a <- readPrimArray marr ix+ writePrimArray marr ix a+ unsafeFreezePrimArray marr+ return (arr1 == arr2)++primPutPutByteArray :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property+primPutPutByteArray _ = property $ \(a :: a) (as :: [a]) -> (not (L.null as)) ==> do+ let arr1 = primArrayFromList as :: PrimArray a+ len = L.length as+ ix <- choose (0,len - 1)+ (arr2,arr3) <- return $ runST $ do+ marr2 <- newPrimArray len+ copyPrimArray marr2 0 arr1 0 len+ writePrimArray marr2 ix a+ marr3 <- newPrimArray len+ copyMutablePrimArray marr3 0 marr2 0 len+ arr2 <- unsafeFreezePrimArray marr2+ writePrimArray marr3 ix a+ arr3 <- unsafeFreezePrimArray marr3+ return (arr2,arr3)+ return (arr2 == arr3)++primPutGetAddr :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property+primPutGetAddr _ = property $ \(a :: a) len -> (len > 0) ==> do+ ix <- choose (0,len - 1)+ return $ unsafePerformIO $ do+ ptr@(Ptr addr#) :: Ptr a <- mallocBytes (len * P.sizeOf (undefined :: a))+ let addr = Addr addr#+ writeOffAddr addr ix a+ a' <- readOffAddr addr ix+ free ptr+ return (a == a')++primGetPutAddr :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property+primGetPutAddr _ = property $ \(as :: [a]) -> (not (L.null as)) ==> do+ let arr1 = primArrayFromList as :: PrimArray a+ len = L.length as+ ix <- choose (0,len - 1)+ arr2 <- return $ unsafePerformIO $ do+ ptr@(Ptr addr#) :: Ptr a <- mallocBytes (len * P.sizeOf (undefined :: a))+ let addr = Addr addr#+ copyPrimArrayToPtr ptr arr1 0 len+ a :: a <- readOffAddr addr ix+ writeOffAddr addr ix a+ marr <- newPrimArray len+ copyPtrToMutablePrimArray marr 0 ptr len+ free ptr+ unsafeFreezePrimArray marr+ return (arr1 == arr2)++primSetByteArray :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property+primSetByteArray _ = property $ \(as :: [a]) (z :: a) -> do+ let arr1 = primArrayFromList as :: PrimArray a+ len = L.length as+ x <- choose (0,len)+ y <- choose (0,len)+ let lo = min x y+ hi = max x y+ return $ runST $ do+ marr2 <- newPrimArray len+ copyPrimArray marr2 0 arr1 0 len+ marr3 <- newPrimArray len+ copyPrimArray marr3 0 arr1 0 len+ setPrimArray marr2 lo (hi - lo) z+ internalDefaultSetPrimArray marr3 lo (hi - lo) z+ arr2 <- unsafeFreezePrimArray marr2+ arr3 <- unsafeFreezePrimArray marr3+ return (arr2 == arr3)++primSetOffAddr :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property+primSetOffAddr _ = property $ \(as :: [a]) (z :: a) -> do+ let arr1 = primArrayFromList as :: PrimArray a+ len = L.length as+ x <- choose (0,len)+ y <- choose (0,len)+ let lo = min x y+ hi = max x y+ return $ unsafePerformIO $ do+ ptrA@(Ptr addrA#) :: Ptr a <- mallocBytes (len * P.sizeOf (undefined :: a))+ let addrA = Addr addrA#+ copyPrimArrayToPtr ptrA arr1 0 len+ ptrB@(Ptr addrB#) :: Ptr a <- mallocBytes (len * P.sizeOf (undefined :: a))+ let addrB = Addr addrB#+ copyPrimArrayToPtr ptrB arr1 0 len+ setOffAddr addrA lo (hi - lo) z+ internalDefaultSetOffAddr addrB lo (hi - lo) z+ marrA <- newPrimArray len+ copyPtrToMutablePrimArray marrA 0 ptrA len+ free ptrA+ marrB <- newPrimArray len+ copyPtrToMutablePrimArray marrB 0 ptrB len+ free ptrB+ arrA <- unsafeFreezePrimArray marrA+ arrB <- unsafeFreezePrimArray marrB+ return (arrA == arrB)++-- byte array with phantom variable that specifies element type+data PrimArray a = PrimArray ByteArray#+data MutablePrimArray s a = MutablePrimArray (MutableByteArray# s)++instance (Eq a, Prim a) => Eq (PrimArray a) where+ a1 == a2 = sizeofPrimArray a1 == sizeofPrimArray a2 && loop (sizeofPrimArray a1 - 1)+ where+ loop !i | i < 0 = True+ | otherwise = indexPrimArray a1 i == indexPrimArray a2 i && loop (i-1)++#if MIN_VERSION_base(4,7,0)+instance Prim a => IsList (PrimArray a) where+ type Item (PrimArray a) = a+ fromList = primArrayFromList+ fromListN = primArrayFromListN+ toList = primArrayToList+#endif++indexPrimArray :: forall a. Prim a => PrimArray a -> Int -> a+indexPrimArray (PrimArray arr#) (I# i#) = indexByteArray# arr# i#++sizeofPrimArray :: forall a. Prim a => PrimArray a -> Int+sizeofPrimArray (PrimArray arr#) = I# (quotInt# (sizeofByteArray# arr#) (P.sizeOf# (undefined :: a)))++newPrimArray :: forall m a. (PrimMonad m, Prim a) => Int -> m (MutablePrimArray (PrimState m) a)+newPrimArray (I# n#)+ = primitive (\s# ->+ case newByteArray# (n# *# sizeOf# (undefined :: a)) s# of+ (# s'#, arr# #) -> (# s'#, MutablePrimArray arr# #)+ )++readPrimArray :: (Prim a, PrimMonad m) => MutablePrimArray (PrimState m) a -> Int -> m a+readPrimArray (MutablePrimArray arr#) (I# i#)+ = primitive (readByteArray# arr# i#)++writePrimArray ::+ (Prim a, PrimMonad m)+ => MutablePrimArray (PrimState m) a+ -> Int+ -> a+ -> m ()+writePrimArray (MutablePrimArray arr#) (I# i#) x+ = primitive_ (writeByteArray# arr# i# x)++unsafeFreezePrimArray+ :: PrimMonad m => MutablePrimArray (PrimState m) a -> m (PrimArray a)+unsafeFreezePrimArray (MutablePrimArray arr#)+ = primitive (\s# -> case unsafeFreezeByteArray# arr# s# of+ (# s'#, arr'# #) -> (# s'#, PrimArray arr'# #))++#if !MIN_VERSION_base(4,7,0)+ptrToAddr :: Ptr a -> Addr+ptrToAddr (Ptr x) = Addr x++generateM_ :: Monad m => Int -> (Int -> m a) -> m ()+generateM_ n f = go 0 where+ go !ix = if ix < n+ then f ix >> go (ix + 1)+ else return ()+#endif++copyPrimArrayToPtr :: forall m a. (PrimMonad m, Prim a)+ => Ptr a -- ^ destination pointer+ -> PrimArray a -- ^ source array+ -> Int -- ^ offset into source array+ -> Int -- ^ number of prims to copy+ -> m ()+#if MIN_VERSION_base(4,7,0)+copyPrimArrayToPtr (Ptr addr#) (PrimArray ba#) (I# soff#) (I# n#) =+ primitive (\ s# ->+ let s'# = copyByteArrayToAddr# ba# (soff# *# siz#) addr# (n# *# siz#) s#+ in (# s'#, () #))+ where siz# = sizeOf# (undefined :: a)+#else+copyPrimArrayToPtr addr ba soff n =+ generateM_ n $ \ix -> writeOffAddr (ptrToAddr addr) ix (indexPrimArray ba (ix + soff))+#endif++copyPtrToMutablePrimArray :: forall m a. (PrimMonad m, Prim a)+ => MutablePrimArray (PrimState m) a+ -> Int+ -> Ptr a+ -> Int+ -> m ()+#if MIN_VERSION_base(4,7,0)+copyPtrToMutablePrimArray (MutablePrimArray ba#) (I# doff#) (Ptr addr#) (I# n#) =+ primitive (\ s# ->+ let s'# = copyAddrToByteArray# addr# ba# (doff# *# siz#) (n# *# siz#) s#+ in (# s'#, () #))+ where siz# = sizeOf# (undefined :: a)+#else+copyPtrToMutablePrimArray ba doff addr n =+ generateM_ n $ \ix -> do+ x <- readOffAddr (ptrToAddr addr) ix+ writePrimArray ba (doff + ix) x+#endif++copyMutablePrimArray :: forall m a.+ (PrimMonad m, Prim a)+ => MutablePrimArray (PrimState m) a -- ^ destination array+ -> Int -- ^ offset into destination array+ -> MutablePrimArray (PrimState m) a -- ^ source array+ -> Int -- ^ offset into source array+ -> Int -- ^ number of bytes to copy+ -> m ()+copyMutablePrimArray (MutablePrimArray dst#) (I# doff#) (MutablePrimArray src#) (I# soff#) (I# n#)+ = primitive_ (copyMutableByteArray#+ src#+ (soff# *# (sizeOf# (undefined :: a)))+ dst#+ (doff# *# (sizeOf# (undefined :: a)))+ (n# *# (sizeOf# (undefined :: a)))+ )++copyPrimArray :: forall m a.+ (PrimMonad m, Prim a)+ => MutablePrimArray (PrimState m) a -- ^ destination array+ -> Int -- ^ offset into destination array+ -> PrimArray a -- ^ source array+ -> Int -- ^ offset into source array+ -> Int -- ^ number of bytes to copy+ -> m ()+copyPrimArray (MutablePrimArray dst#) (I# doff#) (PrimArray src#) (I# soff#) (I# n#)+ = primitive_ (copyByteArray#+ src#+ (soff# *# (sizeOf# (undefined :: a)))+ dst#+ (doff# *# (sizeOf# (undefined :: a)))+ (n# *# (sizeOf# (undefined :: a)))+ )++setPrimArray+ :: (Prim a, PrimMonad m)+ => MutablePrimArray (PrimState m) a -- ^ array to fill+ -> Int -- ^ offset into array+ -> Int -- ^ number of values to fill+ -> a -- ^ value to fill with+ -> m ()+setPrimArray (MutablePrimArray dst#) (I# doff#) (I# sz#) x+ = primitive_ (P.setByteArray# dst# doff# sz# x)++primArrayFromList :: Prim a => [a] -> PrimArray a+primArrayFromList xs = primArrayFromListN (L.length xs) xs++primArrayFromListN :: forall a. Prim a => Int -> [a] -> PrimArray a+primArrayFromListN len vs = runST run where+ run :: forall s. ST s (PrimArray a)+ run = do+ arr <- newPrimArray len+ let go :: [a] -> Int -> ST s ()+ go !xs !ix = case xs of+ [] -> return ()+ a : as -> do+ writePrimArray arr ix a+ go as (ix + 1)+ go vs 0+ unsafeFreezePrimArray arr++primArrayToList :: forall a. Prim a => PrimArray a -> [a]+primArrayToList arr = go 0 where+ !len = sizeofPrimArray arr+ go :: Int -> [a]+ go !ix = if ix < len+ then indexPrimArray arr ix : go (ix + 1)+ else []++#if MIN_VERSION_base(4,7,0)+primListByteArray :: forall a. (Prim a, Eq a, Arbitrary a, Show a) => Proxy a -> Property+primListByteArray _ = property $ \(as :: [a]) ->+ as == toList (fromList as :: PrimArray a)+#endif++setOffAddr :: forall a. Prim a => Addr -> Int -> Int -> a -> IO ()+setOffAddr addr ix len a = setAddr (plusAddr addr (P.sizeOf (undefined :: a) * ix)) len a++internalDefaultSetPrimArray :: Prim a+ => MutablePrimArray s a -> Int -> Int -> a -> ST s ()+internalDefaultSetPrimArray (MutablePrimArray arr) (I# i) (I# len) ident =+ primitive_ (internalDefaultSetByteArray# arr i len ident)++internalDefaultSetByteArray# :: Prim a+ => MutableByteArray# s -> Int# -> Int# -> a -> State# s -> State# s+internalDefaultSetByteArray# arr# i# len# ident = go 0#+ where+ go ix# s0 = if isTrue# (ix# <# len#)+ then case writeByteArray# arr# (i# +# ix#) ident s0 of+ s1 -> go (ix# +# 1#) s1+ else s0++internalDefaultSetOffAddr :: Prim a => Addr -> Int -> Int -> a -> IO ()+internalDefaultSetOffAddr (Addr addr) (I# ix) (I# len) a = primitive_+ (internalDefaultSetOffAddr# addr ix len a)++internalDefaultSetOffAddr# :: Prim a => Addr# -> Int# -> Int# -> a -> State# s -> State# s+internalDefaultSetOffAddr# addr# i# len# ident = go 0#+ where+ go ix# s0 = if isTrue# (ix# <# len#)+ then case writeOffAddr# addr# (i# +# ix#) ident s0 of+ s1 -> go (ix# +# 1#) s1+ else s0
+ src/Test/QuickCheck/Classes/Ring.hs view
@@ -0,0 +1,43 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE ScopedTypeVariables #-}++{-# OPTIONS_GHC -Wall #-}++module Test.QuickCheck.Classes.Ring+ ( +#if HAVE_SEMIRINGS+ ringLaws+#endif+ ) where++#if HAVE_SEMIRINGS+import Data.Semiring+import Prelude hiding (Num(..))+#endif++import Data.Proxy (Proxy)+import Test.QuickCheck hiding ((.&.))+import Test.QuickCheck.Property (Property)++import Test.QuickCheck.Classes.Internal (Laws(..), myForAllShrink)++#if HAVE_SEMIRINGS+-- | Tests the following properties:+--+-- [/Additive Inverse/]+-- @'negate' a '+' a ≡ 0@+--+-- Note that this does not test any of the laws tested by 'Test.QuickCheck.Classes.Semiring.semiringLaws'.+ringLaws :: (Ring a, Eq a, Arbitrary a, Show a) => Proxy a -> Laws+ringLaws p = Laws "Ring"+ [ ("Additive Inverse", ringAdditiveInverse p)+ ]++ringAdditiveInverse :: forall a. (Ring a, Eq a, Arbitrary a, Show a) => Proxy a -> Property+ringAdditiveInverse _ = myForAllShrink True (const True)+ (\(a :: a) -> ["a = " ++ show a])+ "negate a + a"+ (\a -> negate a + a)+ "0"+ (const zero)+#endif
+ src/Test/QuickCheck/Classes/Semigroupoid.hs view
@@ -0,0 +1,81 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE ScopedTypeVariables #-}++#if HAVE_QUANTIFIED_CONSTRAINTS+{-# LANGUAGE QuantifiedConstraints #-}+#endif++{-# OPTIONS_GHC -Wall #-}++module Test.QuickCheck.Classes.Semigroupoid+ (+#if defined(HAVE_SEMIGROUPOIDS) && defined(HAVE_BINARY_LAWS)+ semigroupoidLaws+ , commutativeSemigroupoidLaws+#endif+ ) where++#if defined(HAVE_SEMIGROUPOIDS) && defined(HAVE_BINARY_LAWS)+import Prelude hiding (id, (.))+import Data.Semigroupoid (Semigroupoid(..))+import Test.QuickCheck hiding ((.&.))+import Data.Functor.Classes (Eq2,Show2)+import Test.QuickCheck.Property (Property)++import Test.QuickCheck.Classes.Internal++-- | Tests the following 'Semigroupoid' properties:+--+-- [/Associativity/]+-- @f `'o'` (g `'o'` h) ≡ (f `'o'` g) `'o'` h@+--+-- /Note/: This property test is only available when this package is built with+-- @base-4.9+@ or @transformers-0.5+@.+semigroupoidLaws :: forall proxy s.+#if HAVE_QUANTIFIED_CONSTRAINTS+ (Semigroupoid s, forall a b. (Eq a, Eq b) => Eq (s a b), forall a b. (Show a, Show b) => Show (s a b), forall a b. (Arbitrary a, Arbitrary b) => Arbitrary (s a b))+#else+ (Semigroupoid s, Eq2 s, Show2 s, Arbitrary2 s)+#endif+ => proxy s -> Laws+semigroupoidLaws p = Laws "Semigroupoid"+ [ ("Associativity", semigroupoidAssociativity p)+ ]++-- | Tests everything from 'semigroupoidLaws' plus the following:+--+-- [/Commutative/]+-- @f `'o'` g ≡ g `'o'` f@+--+-- /Note/: This property test is only available when this package is built with+-- @base-4.9+@ or @transformers-0.5+@.+commutativeSemigroupoidLaws :: forall proxy s.+#if HAVE_QUANTIFIED_CONSTRAINTS+ (Semigroupoid s, forall a b. (Eq a, Eq b) => Eq (s a b), forall a b. (Show a, Show b) => Show (s a b), forall a b. (Arbitrary a, Arbitrary b) => Arbitrary (s a b))+#else+ (Semigroupoid s, Eq2 s, Show2 s, Arbitrary2 s)+#endif+ => proxy s -> Laws+commutativeSemigroupoidLaws p = Laws "Commutative Semigroupoid" $ lawsProperties (semigroupoidLaws p) +++ [ ("Commutative", semigroupoidCommutativity p)+ ]++semigroupoidAssociativity :: forall proxy s.+#if HAVE_QUANTIFIED_CONSTRAINTS+ (Semigroupoid s, forall a b. (Eq a, Eq b) => Eq (s a b), forall a b. (Show a, Show b) => Show (s a b), forall a b. (Arbitrary a, Arbitrary b) => Arbitrary (s a b))+#else+ (Semigroupoid s, Eq2 s, Show2 s, Arbitrary2 s)+#endif+ => proxy s -> Property+semigroupoidAssociativity _ = property $ \(Apply2 (f :: s Integer Integer)) (Apply2 (g :: s Integer Integer)) (Apply2 (h :: s Integer Integer)) -> eq2 (f `o` (g `o` h)) ((f `o` g) `o` h)++semigroupoidCommutativity :: forall proxy s.+#if HAVE_QUANTIFIED_CONSTRAINTS+ (Semigroupoid s, forall a b. (Eq a, Eq b) => Eq (s a b), forall a b. (Show a, Show b) => Show (s a b), forall a b. (Arbitrary a, Arbitrary b) => Arbitrary (s a b))+#else+ (Semigroupoid s, Eq2 s, Show2 s, Arbitrary2 s)+#endif+ => proxy s -> Property+semigroupoidCommutativity _ = property $ \(Apply2 (f :: s Integer Integer)) (Apply2 (g :: s Integer Integer)) -> eq2 (f `o` g) (g `o` f)++#endif
+ src/Test/QuickCheck/Classes/Semiring.hs view
@@ -0,0 +1,191 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE ScopedTypeVariables #-}++{-# OPTIONS_GHC -Wall #-}++module Test.QuickCheck.Classes.Semiring+ (+#if HAVE_SEMIRINGS+ semiringLaws+#endif+ ) where++#if HAVE_SEMIRINGS+import Data.Semiring hiding (fromInteger)+import Prelude hiding (Num(..))+import Prelude (fromInteger)+#endif++import Data.Proxy (Proxy)+import Test.QuickCheck hiding ((.&.))+import Test.QuickCheck.Property (Property)++import Test.QuickCheck.Classes.Internal (Laws(..), myForAllShrink)++#if HAVE_SEMIRINGS+-- | Tests the following properties:+--+-- [/Additive Commutativity/]+-- @a + b ≡ b + a@+-- [/Additive Left Identity/]+-- @0 + a ≡ a@+-- [/Additive Right Identity/]+-- @a + 0 ≡ a@+-- [/Multiplicative Associativity/]+-- @a * (b * c) ≡ (a * b) * c@+-- [/Multiplicative Left Identity/]+-- @1 * a ≡ a@+-- [/Multiplicative Right Identity/]+-- @a * 1 ≡ a@+-- [/Multiplication Left Distributes Over Addition/]+-- @a * (b + c) ≡ (a * b) + (a * c)@+-- [/Multiplication Right Distributes Over Addition/]+-- @(a + b) * c ≡ (a * c) + (b * c)@+-- [/Multiplicative Left Annihilation/]+-- @0 * a ≡ 0@+-- [/Multiplicative Right Annihilation/]+-- @a * 0 ≡ 0@+--+-- Also tests that 'fromNatural' is a homomorphism of semirings:+--+-- [/FromNatural Maps Zero/]+-- 'fromNatural' 0 = 'zero'+-- [/FromNatural Maps One/]+-- 'fromNatural' 1 = 'one'+-- [/FromNatural Maps Plus/]+-- 'fromNatural' (@a@ + @b@) = 'fromNatural' @a@ + 'fromNatural' @b@+-- [/FromNatural Maps Times/]+-- 'fromNatural' (@a@ * @b@) = 'fromNatural' @a@ * 'fromNatural' @b@+semiringLaws :: (Semiring a, Eq a, Arbitrary a, Show a) => Proxy a -> Laws+semiringLaws p = Laws "Semiring"+ [ ("Additive Commutativity", semiringCommutativePlus p)+ , ("Additive Left Identity", semiringLeftIdentityPlus p)+ , ("Additive Right Identity", semiringRightIdentityPlus p)+ , ("Multiplicative Associativity", semiringAssociativeTimes p)+ , ("Multiplicative Left Identity", semiringLeftIdentityTimes p)+ , ("Multiplicative Right Identity", semiringRightIdentityTimes p)+ , ("Multiplication Left Distributes Over Addition", semiringLeftMultiplicationDistributes p)+ , ("Multiplication Right Distributes Over Addition", semiringRightMultiplicationDistributes p)+ , ("Multiplicative Left Annihilation", semiringLeftAnnihilation p)+ , ("Multiplicative Right Annihilation", semiringRightAnnihilation p)+ , ("FromNatural Maps Zero", semiringFromNaturalMapsZero p)+ , ("FromNatural Maps One", semiringFromNaturalMapsOne p)+ , ("FromNatural Maps Plus", semiringFromNaturalMapsPlus p)+ , ("FromNatural Maps Times", semiringFromNaturalMapsTimes p)+ ]++semiringLeftMultiplicationDistributes :: forall a. (Semiring a, Eq a, Arbitrary a, Show a) => Proxy a -> Property+semiringLeftMultiplicationDistributes _ = myForAllShrink True (const True)+ (\(a :: a,b,c) -> ["a = " ++ show a, "b = " ++ show b, "c = " ++ show c])+ "a * (b + c)"+ (\(a,b,c) -> a * (b + c))+ "(a * b) + (a * c)"+ (\(a,b,c) -> (a * b) + (a * c))++semiringRightMultiplicationDistributes :: forall a. (Semiring a, Eq a, Arbitrary a, Show a) => Proxy a -> Property+semiringRightMultiplicationDistributes _ = myForAllShrink True (const True)+ (\(a :: a,b,c) -> ["a = " ++ show a, "b = " ++ show b, "c = " ++ show c])+ "(a + b) * c"+ (\(a,b,c) -> (a + b) * c)+ "(a * c) + (b * c)"+ (\(a,b,c) -> (a * c) + (b * c))++semiringLeftIdentityPlus :: forall a. (Semiring a, Eq a, Arbitrary a, Show a) => Proxy a -> Property+semiringLeftIdentityPlus _ = myForAllShrink False (const True)+ (\(a :: a) -> ["a = " ++ show a])+ "0 + a"+ (\a -> zero + a)+ "a"+ (\a -> a)++semiringRightIdentityPlus :: forall a. (Semiring a, Eq a, Arbitrary a, Show a) => Proxy a -> Property+semiringRightIdentityPlus _ = myForAllShrink False (const True)+ (\(a :: a) -> ["a = " ++ show a])+ "a + 0"+ (\a -> a + zero)+ "a"+ (\a -> a)++semiringRightIdentityTimes :: forall a. (Semiring a, Eq a, Arbitrary a, Show a) => Proxy a -> Property+semiringRightIdentityTimes _ = myForAllShrink False (const True)+ (\(a :: a) -> ["a = " ++ show a])+ "a * 1"+ (\a -> a * one)+ "a"+ (\a -> a)++semiringLeftIdentityTimes :: forall a. (Semiring a, Eq a, Arbitrary a, Show a) => Proxy a -> Property+semiringLeftIdentityTimes _ = myForAllShrink False (const True)+ (\(a :: a) -> ["a = " ++ show a])+ "1 * a"+ (\a -> one * a)+ "a"+ (\a -> a)++semiringLeftAnnihilation :: forall a. (Semiring a, Eq a, Arbitrary a, Show a) => Proxy a -> Property+semiringLeftAnnihilation _ = myForAllShrink False (const True)+ (\(a :: a) -> ["a = " ++ show a])+ "0 * a"+ (\a -> zero * a)+ "0"+ (\_ -> zero)++semiringRightAnnihilation :: forall a. (Semiring a, Eq a, Arbitrary a, Show a) => Proxy a -> Property+semiringRightAnnihilation _ = myForAllShrink False (const True)+ (\(a :: a) -> ["a = " ++ show a])+ "a * 0"+ (\a -> a * zero)+ "0"+ (\_ -> zero)++semiringCommutativePlus :: forall a. (Semiring a, Eq a, Arbitrary a, Show a) => Proxy a -> Property+semiringCommutativePlus _ = myForAllShrink True (const True)+ (\(a :: a,b) -> ["a = " ++ show a, "b = " ++ show b])+ "a + b"+ (\(a,b) -> a + b)+ "b + a"+ (\(a,b) -> b + a)++semiringAssociativeTimes :: forall a. (Semiring a, Eq a, Arbitrary a, Show a) => Proxy a -> Property+semiringAssociativeTimes _ = myForAllShrink True (const True)+ (\(a :: a,b,c) -> ["a = " ++ show a, "b = " ++ show b, "c = " ++ show c])+ "a * (b * c)"+ (\(a,b,c) -> a * (b * c))+ "(a * b) * c"+ (\(a,b,c) -> (a * b) * c)++semiringFromNaturalMapsZero :: forall a. (Semiring a, Eq a, Arbitrary a, Show a) => Proxy a -> Property+semiringFromNaturalMapsZero _ = myForAllShrink False (const True)+ (\_ -> [""])+ "fromNatural 0"+ (\() -> fromNatural 0 :: a)+ "zero"+ (\() -> zero)++semiringFromNaturalMapsOne :: forall a. (Semiring a, Eq a, Arbitrary a, Show a) => Proxy a -> Property+semiringFromNaturalMapsOne _ = myForAllShrink False (const True)+ (\_ -> [""])+ "fromNatural 1"+ (\() -> fromNatural 1 :: a)+ "one"+ (\() -> one)++-- | There is no Arbitrary instance for Natural in QuickCheck,+-- so we use NonNegative Integer instead.+semiringFromNaturalMapsPlus :: forall a. (Semiring a, Eq a, Arbitrary a, Show a) => Proxy a -> Property+semiringFromNaturalMapsPlus _ = myForAllShrink True (const True)+ (\(NonNegative a, NonNegative b) -> ["a = " ++ show a, "b = " ++ show b])+ "fromNatural (a + b)"+ (\(NonNegative a, NonNegative b) -> fromNatural (fromInteger (a + b)) :: a)+ "fromNatural a + fromNatural b"+ (\(NonNegative a, NonNegative b) -> fromNatural (fromInteger a) + fromNatural (fromInteger b))++semiringFromNaturalMapsTimes :: forall a. (Semiring a, Eq a, Arbitrary a, Show a) => Proxy a -> Property+semiringFromNaturalMapsTimes _ = myForAllShrink True (const True)+ (\(NonNegative a, NonNegative b) -> ["a = " ++ show a, "b = " ++ show b])+ "fromNatural (a * b)"+ (\(NonNegative a, NonNegative b) -> fromNatural (fromInteger (a * b)) :: a)+ "fromNatural a * fromNatural b"+ (\(NonNegative a, NonNegative b) -> fromNatural (fromInteger a) * fromNatural (fromInteger b))++#endif
+ test/Advanced.hs view
@@ -0,0 +1,193 @@+{-# language DerivingStrategies #-}+{-# language DerivingVia #-}+{-# language GeneralizedNewtypeDeriving #-}+{-# language LambdaCase #-}+{-# language ScopedTypeVariables #-}+{-# language TypeApplications #-}++import Test.Tasty (TestTree,defaultMain,testGroup,adjustOption)+import Test.QuickCheck (Arbitrary)+import Data.Proxy (Proxy(..))+import Data.Set (Set)+import Data.Primitive (Array)+import Control.Monad (forM_,replicateM)+import Data.Monoid (All(..))+import Test.QuickCheck.Classes (eqLaws,ordLaws)+import Data.Typeable (Typeable,typeRep)+import Data.Coerce (coerce)+import Data.Set (Set)++import qualified Data.Set as S+import qualified Data.List as L+import qualified GHC.Exts as E+import qualified Test.QuickCheck as QC+import qualified Test.Tasty.QuickCheck as TQC+import qualified Test.QuickCheck.Classes as QCC++main :: IO ()+main = defaultMain tests++tests :: TestTree+tests = testGroup "universe"+ [ testGroup "deriving"+ [ testGroup "strict"+ [ laws @A [eqLaws,ordLaws]+ , laws @B [eqLaws,ordLaws]+ , laws @C [eqLaws,ordLaws]+ , laws @D [eqLaws,ordLaws]+ , laws @E [eqLaws,ordLaws]+ , laws @F [eqLaws,ordLaws]+ , laws @G [eqLaws,ordLaws]+ , laws @H [eqLaws,ordLaws]+ , laws @I [eqLaws,ordLaws]+ , laws @K [eqLaws,ordLaws]+ ]+ , testGroup "thunk"+ [ laws @(Thunk A) [eqLaws,ordLaws]+ , laws @(Thunk B) [eqLaws,ordLaws]+ , laws @(Thunk C) [eqLaws,ordLaws]+ , laws @(Thunk D) [eqLaws,ordLaws]+ , laws @(Thunk E) [eqLaws,ordLaws]+ , laws @(Thunk F) [eqLaws,ordLaws]+ , laws @(Thunk G) [eqLaws,ordLaws]+ , laws @(Thunk H) [eqLaws,ordLaws]+ , laws @(Thunk I) [eqLaws,ordLaws]+ , laws @(Thunk K) [eqLaws,ordLaws]+ ]+ , testGroup "lazy"+ [ laws @(Lazy A) [eqLaws,ordLaws]+ , laws @(Lazy B) [eqLaws,ordLaws]+ , laws @(Lazy C) [eqLaws,ordLaws]+ , laws @(Lazy D) [eqLaws,ordLaws]+ , laws @(Lazy E) [eqLaws,ordLaws]+ , laws @(Lazy F) [eqLaws,ordLaws]+ , laws @(Lazy G) [eqLaws,ordLaws]+ , laws @(Lazy H) [eqLaws,ordLaws]+ , laws @(Lazy I) [eqLaws,ordLaws]+ , laws @(Lazy K) [eqLaws,ordLaws]+ ]+ ]+ , testGroup "containers"+ [ testGroup "strict"+ [ laws @(Set A) [eqLaws,ordLaws]+ , laws @(Set B) [eqLaws,ordLaws]+ , laws @(Set C) [eqLaws,ordLaws]+ , laws @(Set D) [eqLaws,ordLaws]+ , laws @(Set E) [eqLaws,ordLaws]+ , laws @(Set F) [eqLaws,ordLaws]+ , laws @(Set G) [eqLaws,ordLaws]+ , laws @(Set H) [eqLaws,ordLaws]+ , laws @(Set I) [eqLaws,ordLaws]+ , laws @(Set K) [eqLaws,ordLaws]+ ]+ , testGroup "lazy"+ [ laws @(SmallLazySet A) [eqLaws,ordLaws]+ , laws @(SmallLazySet B) [eqLaws,ordLaws]+ , laws @(SmallLazySet C) [eqLaws,ordLaws]+ , laws @(SmallLazySet D) [eqLaws,ordLaws]+ , laws @(SmallLazySet E) [eqLaws,ordLaws]+ , laws @(SmallLazySet F) [eqLaws,ordLaws]+ , laws @(SmallLazySet G) [eqLaws,ordLaws]+ , laws @(SmallLazySet H) [eqLaws,ordLaws]+ , laws @(SmallLazySet I) [eqLaws,ordLaws]+ , laws @(SmallLazySet K) [eqLaws,ordLaws]+ ]+ ]+ ]++data A = A0+ deriving stock (Eq,Ord,Show,Read,Bounded,Enum)+ deriving Arbitrary via (Enumeration A)++data B = B0 | B1+ deriving stock (Eq,Ord,Show,Read,Bounded,Enum)+ deriving Arbitrary via (Enumeration B)++data C = C0 | C1 | C2+ deriving stock (Eq,Ord,Show,Read,Bounded,Enum)+ deriving Arbitrary via (Enumeration C)++data D = D0 | D1 | D2 | D3+ deriving stock (Eq,Ord,Show,Read,Bounded,Enum)+ deriving Arbitrary via (Enumeration D)++data E = E0 | E1 | E2 | E3 | E4+ deriving stock (Eq,Ord,Show,Read,Bounded,Enum)+ deriving Arbitrary via (Enumeration E)++data F = F0 | F1 | F2 | F3 | F4 | F5+ deriving stock (Eq,Ord,Show,Read,Bounded,Enum)+ deriving Arbitrary via (Enumeration F)++data G = G0 | G1 | G2 | G3 | G4 | G5 | G6+ deriving stock (Eq,Ord,Show,Read,Bounded,Enum)+ deriving Arbitrary via (Enumeration G)++data H = H0 | H1 | H2 | H3 | H4 | H5 | H6 | H7+ deriving stock (Eq,Ord,Show,Read,Bounded,Enum)+ deriving Arbitrary via (Enumeration H)++data I = I0 | I1 | I2 | I3 | I4 | I5 | I6 | I7 | I8+ deriving stock (Eq,Ord,Show,Read,Bounded,Enum)+ deriving Arbitrary via (Enumeration I)++data J = J0 | J1 | J2 | J3 | J4 | J5 | J6 | J7 | J8 | J9+ deriving stock (Eq,Ord,Show,Read,Bounded,Enum)+ deriving Arbitrary via (Enumeration J)++data K = K0 | K1 | K2 | K3 | K4 | K5 | K6 | K7 | K8 | K9 | K10+ deriving stock (Eq,Ord,Show,Read,Bounded,Enum)+ deriving Arbitrary via (Enumeration K)++laws :: forall a. Typeable a => [Proxy a -> QCC.Laws] -> TestTree+laws = testGroup (show (typeRep (Proxy :: Proxy a))) . map+ ( \f -> let QCC.Laws name pairs = f (Proxy :: Proxy a) in+ testGroup name (map (uncurry TQC.testProperty) pairs)+ )++newtype Enumeration a = Enumeration a++instance (Bounded a, Enum a, Eq a) => Arbitrary (Enumeration a) where+ arbitrary = fmap Enumeration TQC.arbitraryBoundedEnum+ shrink (Enumeration x) = if x == minBound+ then []+ else [Enumeration (pred x)]++data Thunk a = Thunk a+ deriving stock (Eq,Ord,Show,Read)++newtype Lazy a = Lazy a+ deriving newtype (Eq,Ord,Show,Read)++newtype SmallLazySet a = SmallLazySet (Set a)+ deriving newtype (Eq,Ord,Show,Read)++instance Arbitrary a => Arbitrary (Thunk a) where+ arbitrary = do+ a <- TQC.arbitrary+ let {-# NOINLINE b #-}+ b () = a+ pure (Thunk (b ()))+ shrink (Thunk x) = map Thunk (TQC.shrink x)++instance Arbitrary a => Arbitrary (Lazy a) where+ arbitrary = do+ a <- TQC.arbitrary+ let {-# NOINLINE b #-}+ b () = a+ pure (Lazy (b ()))+ shrink (Lazy x) = map Lazy (TQC.shrink x)++instance (Arbitrary a, Ord a) => Arbitrary (SmallLazySet a) where+ arbitrary = do+ a <- TQC.arbitrary+ b <- TQC.arbitrary+ c <- TQC.arbitrary+ let {-# NOINLINE a' #-}+ a' () = a+ let {-# NOINLINE b' #-}+ b' () = b+ let {-# NOINLINE c' #-}+ c' () = c+ pure (SmallLazySet (S.fromList [a' (), b' (), c' (), a' (), b' (), c' ()]))+
test/Spec.hs view
@@ -1,76 +1,260 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE KindSignatures #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE DeriveTraversable #-} -import Test.QuickCheck-import Data.Proxy-import Data.Word-import Data.Int+#if HAVE_QUANTIFIED_CONSTRAINTS+{-# LANGUAGE QuantifiedConstraints #-}+#endif+ import Control.Monad-import Data.Primitive+import Control.Monad.Zip (MonadZip)+import Control.Applicative+#if defined(VERSION_aeson)+import Data.Aeson (ToJSON,FromJSON)+#endif+import Data.Bits import Data.Foldable-import Data.Monoid (Sum)-import Foreign.Storable+import Data.Map (Map)+import qualified Data.Map as M+#if MIN_VERSION_containers(0,5,9)+import qualified Data.Map.Merge.Strict as MM+#endif+import Data.Traversable+#if HAVE_SEMIGROUPOIDS+import Data.Functor.Apply (Apply((<.>)))+#endif+#if HAVE_BINARY_LAWS+import Data.Functor.Const (Const(..))+#endif+#if HAVE_UNARY_LAWS import Data.Functor.Classes-import Data.Aeson (ToJSON,FromJSON)+#endif+import Data.Int+import Data.Monoid (Sum(..),Monoid,mappend,mconcat,mempty)+import Data.Orphans ()+import Data.Primitive+import Data.Proxy import Data.Vector (Vector)+import Data.Word+import Foreign.Storable+import Test.QuickCheck+import Text.Show.Functions import qualified Data.Vector as V+import qualified Data.Foldable as F import Test.QuickCheck.Classes+import qualified Spec.ShowRead main :: IO ()-main = lawsCheckMany allPropsApplied+main = do+#if HAVE_SEMIGROUPOIDS+#if MIN_VERSION_containers(0,5,9)+ quickCheck prop_map_apply_equals+#endif+#endif+ lawsCheckMany allPropsApplied allPropsApplied :: [(String,[Laws])]-allPropsApplied = +allPropsApplied = M.toList . M.fromListWith (++) $ [ ("Int",allLaws (Proxy :: Proxy Int)) , ("Int64",allLaws (Proxy :: Proxy Int64)) , ("Word",allLaws (Proxy :: Proxy Word))-#if MIN_VERSION_QuickCheck(2,10,0)- , ("Maybe",allHigherLaws (Proxy :: Proxy Maybe))- , ("List",allHigherLaws (Proxy :: Proxy []))+#if HAVE_BINARY_LAWS+ , ("Tuple"+ , [ bitraversableLaws (Proxy :: Proxy (,))+ , bifoldableLaws (Proxy :: Proxy (,))+ ]+ )+ , ("Const"+ , [ bifoldableLaws (Proxy :: Proxy Const)+ , bitraversableLaws (Proxy :: Proxy Const)+ ]+ )+ , ("Either"+ , [ bitraversableLaws (Proxy :: Proxy Either)+ , bifoldableLaws (Proxy :: Proxy Either)+ ]+ ) #endif- , ("Vector",[isListLaws (Proxy :: Proxy (Vector Word))])+#if HAVE_UNARY_LAWS+ , ("Maybe",allHigherLaws (Proxy1 :: Proxy1 Maybe))+ , ("List",allHigherLaws (Proxy1 :: Proxy1 []))+-- , ("BadList",allHigherLaws (Proxy1 :: Proxy1 BadList))+#endif+#if defined(HAVE_SEMIGROUPOIDS) && defined(HAVE_UNARY_LAWS)+#if MIN_VERSION_base(4,9,0) && MIN_VERSION_containers(0,5,9)+ , ("Map", someHigherLaws (Proxy1 :: Proxy1 (Map Int)))+ , ("Pound", someHigherLaws (Proxy1 :: Proxy1 (Pound Int)))+#endif+#endif+#if MIN_VERSION_base(4,7,0)+ , ("Vector",+ [ isListLaws (Proxy :: Proxy (Vector Word))+#if HAVE_VECTOR+ , muvectorLaws (Proxy :: Proxy Word8)+ , muvectorLaws (Proxy :: Proxy (Int, Word))+#endif+ ])+#endif ]+ ++ Spec.ShowRead.lawsApplied -allLaws :: forall a. (Num a, Prim a, Storable a, Ord a, Arbitrary a, Show a, Read a, ToJSON a, FromJSON a) => Proxy a -> [Laws]-allLaws p = +allLaws :: forall a.+ ( Integral a+ , Num a+ , Prim a+ , Storable a+ , Ord a+ , Arbitrary a+ , Show a+ , Read a+ , Enum a+ , Bounded a+#if defined(VERSION_aeson)+ , ToJSON a+ , FromJSON a+#endif+#if MIN_VERSION_base(4,7,0)+ , FiniteBits a+#endif+ ) => Proxy a -> [Laws]+allLaws p = [ primLaws p , storableLaws p+ , semigroupLaws (Proxy :: Proxy (Sum a)) , monoidLaws (Proxy :: Proxy (Sum a))- , showReadLaws p+ , boundedEnumLaws p+#if defined(VERSION_aeson) , jsonLaws p+#endif , eqLaws p , ordLaws p+ , numLaws p+ , integralLaws p+#if MIN_VERSION_base(4,7,0)+ , bitsLaws p+#endif ] foldlMapM :: (Foldable t, Monoid b, Monad m) => (a -> m b) -> t a -> m b-foldlMapM f = foldlM (\b a -> fmap (mappend b) (f a)) mempty+foldlMapM f = foldlM (\b a -> liftM (mappend b) (f a)) mempty -#if MIN_VERSION_QuickCheck(2,10,0)-allHigherLaws :: (Foldable f, Monad f, Eq1 f, Arbitrary1 f, Show1 f) => Proxy f -> [Laws]-allHigherLaws p = +#if HAVE_UNARY_LAWS+allHigherLaws ::+ (Traversable f, MonadZip f, MonadPlus f, Applicative f,+#if HAVE_QUANTIFIED_CONSTRAINTS+ forall a. Eq a => Eq (f a), forall a. Arbitrary a => Arbitrary (f a),+ forall a. Show a => Show (f a)+#else+ Eq1 f, Arbitrary1 f, Show1 f+#endif+ ) => proxy f -> [Laws]+allHigherLaws p = [ functorLaws p , applicativeLaws p , monadLaws p+ , monadPlusLaws p+ , monadZipLaws p , foldableLaws p+ , traversableLaws p ] #endif --- This type is fails the laws for the strict functions+#if defined(HAVE_SEMIGROUPOIDS) && defined(HAVE_UNARY_LAWS)+someHigherLaws ::+ (Apply f,+#if HAVE_QUANTIFIED_CONSTRAINTS+ forall a. Eq a => Eq (f a), forall a. Arbitrary a => Arbitrary (f a),+ forall a. Show a => Show (f a)+#else+ Eq1 f, Arbitrary1 f, Show1 f+#endif+ ) => proxy f -> [Laws]+someHigherLaws p =+ [ applyLaws p+ ]+#endif++-- This type fails the laws for the strict functions -- in Foldable. It is used just to confirm that -- those property tests actually work.-newtype Rouge a = Rouge [a]- deriving (Eq,Show,Arbitrary,Arbitrary1,Eq1,Show1)+newtype Rogue a = Rogue [a]+ deriving+ ( Eq, Show, Arbitrary+#if HAVE_UNARY_LAWS+ , Arbitrary1+ , Eq1+ , Show1+#endif+ ) -instance Foldable Rouge where- foldMap f (Rouge xs) = foldMap f xs- foldl f x (Rouge xs) = foldl f x xs- foldl' f x (Rouge xs) = foldl f x xs- foldr' f x (Rouge xs) = foldr f x xs+-- Note: when using base < 4.6, the Rogue type does+-- not really test anything.+instance Foldable Rogue where+ foldMap f (Rogue xs) = F.foldMap f xs+ foldl f x (Rogue xs) = F.foldl f x xs+#if MIN_VERSION_base(4,6,0)+ foldl' f x (Rogue xs) = F.foldl f x xs+ foldr' f x (Rogue xs) = F.foldr f x xs+#endif +newtype BadList a = BadList [a]+ deriving+ ( Eq, Show, Arbitrary+ , Arbitrary1, Eq1, Show1+ , Traversable, Functor, MonadZip, Monad, Applicative, MonadPlus, Alternative+ )++instance Foldable BadList where+ foldMap f (BadList xs) = F.foldMap f xs+ fold (BadList xs) = fold (reverse xs)++newtype Pound k v = Pound { getPound :: Map k v }+ deriving+ ( Eq, Functor, Show, Arbitrary+#if HAVE_UNARY_LAWS+ , Arbitrary1+ -- The following instances are only available for the variants+ -- of the type classes in base, not for those in transformers.+#if MIN_VERSION_base(4,9,0) && MIN_VERSION_containers(0,5,9)+ , Eq1+ , Show1+#endif+#endif+ )++#if HAVE_SEMIGROUPOIDS+#if MIN_VERSION_containers(0,5,9)+instance Ord k => Apply (Pound k) where+ Pound m1 <.> Pound m2 = Pound $+ MM.merge+ MM.dropMissing+ MM.dropMissing+ (MM.zipWithMatched (\_ f a -> f a))+ m1+ m2+#endif+#endif++#if HAVE_SEMIGROUPOIDS+#if MIN_VERSION_containers(0,5,9)+prop_map_apply_equals :: Map Int (Int -> Int)+ -> Map Int Int+ -> Bool+prop_map_apply_equals mf ma =+ let pf = Pound mf+ pa = Pound ma+ m = mf <.> ma+ p = pf <.> pa+ in m == (getPound p)+#endif+#endif+ ------------------- -- Orphan Instances -------------------@@ -79,3 +263,14 @@ arbitrary = V.fromList <$> arbitrary shrink v = map V.fromList (shrink (V.toList v)) +#if !MIN_VERSION_QuickCheck(2,8,2)+instance (Ord k, Arbitrary k, Arbitrary v) => Arbitrary (Map k v) where+ arbitrary = M.fromList <$> arbitrary+ shrink m = map M.fromList (shrink (M.toList m))+#endif++#if !MIN_VERSION_QuickCheck(2,9,0)+instance Arbitrary a => Arbitrary (Sum a) where+ arbitrary = Sum <$> arbitrary+ shrink = map Sum . shrink . getSum+#endif
+ test/Spec/ShowRead.hs view
@@ -0,0 +1,121 @@+{-# LANGUAGE CPP #-}+{-# OPTIONS_GHC -Wall #-}++module Spec.ShowRead where++import Control.Applicative (liftA2)+import Data.Complex (Complex)+import Data.Fixed (E0, E1, E12, Fixed, HasResolution)+import Data.Int (Int64, Int8)+import Data.Orphans ()+import Data.Proxy (Proxy(Proxy))+import Data.Ratio (Ratio)+import Data.Word+import Test.QuickCheck (Arbitrary(arbitrary), elements)+#if MIN_VERSION_QuickCheck(2,8,2)+import Data.IntMap (IntMap)+import Data.IntSet (IntSet)+import Data.Map (Map)+import Data.Sequence (Seq)+import Data.Set (Set)+#endif+#if MIN_VERSION_QuickCheck(2,9,0)+import Control.Applicative (Const, ZipList)+import Data.Functor.Constant (Constant)+import Data.Functor.Identity (Identity)+import Data.Version (Version)+#endif+#if MIN_VERSION_QuickCheck(2,10,0)+import Data.Functor.Compose (Compose)+import Data.Functor.Product (Product)+#endif++import Test.QuickCheck.Classes++data Prefix = Prefix | Prefix' | Prefix_+ deriving (Eq, Read, Show)++instance Arbitrary Prefix where+ arbitrary = elements [Prefix, Prefix', Prefix_]++data WeirdRecord = (:*) { left :: Int, right :: Int }+ deriving (Eq, Read, Show)++instance Arbitrary WeirdRecord where+ arbitrary = liftA2 (:*) arbitrary arbitrary++lawsApplied :: [(String,[Laws])]+lawsApplied =+ [ -- local+ ("Prefix", allShowReadLaws (Proxy :: Proxy Prefix))+ , ("WeirdRecord", allShowReadLaws (Proxy :: Proxy WeirdRecord))++ -- base+ , ("()", allShowReadLaws (Proxy :: Proxy ()))+ , ("Bool", allShowReadLaws (Proxy :: Proxy Bool))+ , ("Char", allShowReadLaws (Proxy :: Proxy Char))+ , ("Complex Float", allShowReadLaws (Proxy :: Proxy (Complex Float)))+ , ("Complex Double", allShowReadLaws (Proxy :: Proxy (Complex Double)))+ , ("Double", allShowReadLaws (Proxy :: Proxy Double))+ , ("Either", allShowReadLaws (Proxy :: Proxy (Either Int Int)))+ , ("Fixed E12", allFixedLaws (Proxy :: Proxy (Fixed E12)))+ -- , ("Fixed E9", allFixedLaws (Proxy :: Proxy (Fixed E9)))+ -- , ("Fixed E6", allFixedLaws (Proxy :: Proxy (Fixed E6)))+ -- , ("Fixed E3", allFixedLaws (Proxy :: Proxy (Fixed E3)))+ -- , ("Fixed E2", allFixedLaws (Proxy :: Proxy (Fixed E2)))+ , ("Fixed E1", allFixedLaws (Proxy :: Proxy (Fixed E1)))+ , ("Fixed E0", allFixedLaws (Proxy :: Proxy (Fixed E0)))+ , ("Float", allShowReadLaws (Proxy :: Proxy Float))+ , ("Int", allShowReadLaws (Proxy :: Proxy Int))+ -- , ("Int16", allShowReadLaws (Proxy :: Proxy Int16))+ -- , ("Int32", allShowReadLaws (Proxy :: Proxy Int32))+ , ("Int64", allShowReadLaws (Proxy :: Proxy Int64))+ , ("Int8", allShowReadLaws (Proxy :: Proxy Int8))+ , ("Integer", allShowReadLaws (Proxy :: Proxy Integer))+ , ("List", allShowReadLaws (Proxy :: Proxy [Int]))+ , ("Maybe", allShowReadLaws (Proxy :: Proxy (Maybe Int)))+ , ("Ordering", allShowReadLaws (Proxy :: Proxy Ordering))+ , ("Ratio", allShowReadLaws (Proxy :: Proxy (Ratio Int)))+ , ("Tuple2", allShowReadLaws (Proxy :: Proxy (Int,Int)))+ , ("Tuple3", allShowReadLaws (Proxy :: Proxy (Int,Int,Int)))+ , ("Word", allShowReadLaws (Proxy :: Proxy Word))+ -- , ("Word16", allShowReadLaws (Proxy :: Proxy Word16))+ -- , ("Word32", allShowReadLaws (Proxy :: Proxy Word32))+ , ("Word64", allShowReadLaws (Proxy :: Proxy Word64))+ , ("Word8", allShowReadLaws (Proxy :: Proxy Word8))+#if MIN_VERSION_QuickCheck(2,9,0)+ , ("Const", allShowReadLaws (Proxy :: Proxy (Const Int Int)))+ , ("Constant", allShowReadLaws (Proxy :: Proxy (Constant Int Int)))+ , ("Identity", allShowReadLaws (Proxy :: Proxy (Identity Int)))+ , ("Version", allShowReadLaws (Proxy :: Proxy Version))+ , ("ZipList", allShowReadLaws (Proxy :: Proxy (ZipList Int)))+#endif+#if MIN_VERSION_QuickCheck(2,10,0)+ , ("Compose", allShowReadLaws (Proxy :: Proxy (Compose [] Maybe Int)))+ , ("Product", allShowReadLaws (Proxy :: Proxy (Product [] Maybe Int)))+#endif++ -- containers+#if MIN_VERSION_QuickCheck(2,8,2)+ , ("IntMap", allShowReadLaws (Proxy :: Proxy (IntMap Int)))+ , ("IntSet", allShowReadLaws (Proxy :: Proxy IntSet))+ , ("Map", allShowReadLaws (Proxy :: Proxy (Map Int Int)))+ , ("Seq", allShowReadLaws (Proxy :: Proxy (Seq Int)))+ , ("Set", allShowReadLaws (Proxy :: Proxy (Set Int)))+#endif+ ]++allShowReadLaws :: (Show a, Read a, Eq a, Arbitrary a) => Proxy a -> [Laws]+allShowReadLaws p = map ($p)+ [ showLaws+ , showReadLaws+ ]++allFixedLaws :: HasResolution e => Proxy (Fixed e) -> [Laws]+allFixedLaws p = map ($p)+ [ showLaws+#if MIN_VERSION_base(4,7,0)+ -- Earlier versions of base have a buggy read instance.+ , showReadLaws+#endif+ ]