quickcheck-classes-base 0.6.0.0 → 0.6.1.0
raw patch · 9 files changed
+130/−16 lines, 9 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Test.QuickCheck.Classes.Base: substitutiveEqLaws :: forall a. (Eq a, Arbitrary a, CoArbitrary a, Function a, Show a) => Proxy a -> Laws
Files
- changelog.md +15/−1
- quickcheck-classes-base.cabal +3/−2
- src/Test/QuickCheck/Classes/Base.hs +2/−1
- src/Test/QuickCheck/Classes/Bifoldable.hs +9/−7
- src/Test/QuickCheck/Classes/Eq.hs +25/−3
- src/Test/QuickCheck/Classes/Integral.hs +22/−0
- src/Test/QuickCheck/Classes/Internal.hs +8/−2
- src/Test/QuickCheck/Classes/Num.hs +33/−0
- src/Test/QuickCheck/Classes/Storable.hs +13/−0
changelog.md view
@@ -4,7 +4,21 @@ 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/). -## [0.6.0.0] - 2019-08-07+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.1.0] - 2020-09-09+### 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.0.0] - 2019-08-08 ### Added - Initial release. This factor out a subset of laws tests from `quickcheck-classes` and depend on this library that
quickcheck-classes-base.cabal view
@@ -1,5 +1,5 @@ name: quickcheck-classes-base-version: 0.6.0.0+version: 0.6.1.0 synopsis: QuickCheck common typeclasses from `base` description: This libary is a minimal variant of `quickcheck-classes` that@@ -87,9 +87,10 @@ , QuickCheck >= 2.7 , transformers >= 0.3 && < 0.6 , containers >= 0.4.2.1- , semigroups >= 0.17 , tagged , fail+ if impl(ghc < 8.0)+ build-depends: semigroups >= 0.17 if impl(ghc > 7.4) && impl(ghc < 7.6) build-depends: ghc-prim if impl(ghc > 8.5)
src/Test/QuickCheck/Classes/Base.hs view
@@ -22,6 +22,7 @@ , bitsLaws #endif , eqLaws+ , substitutiveEqLaws , numLaws , integralLaws , ixLaws@@ -62,7 +63,7 @@ -- ** Binary type constructors , bifoldableLaws , bifunctorLaws- , bitraversableLaws + , bitraversableLaws , categoryLaws , commutativeCategoryLaws #endif
src/Test/QuickCheck/Classes/Bifoldable.hs view
@@ -31,10 +31,10 @@ -- | Tests the following 'Bifunctor' properties: -- -- [/Bifold Identity/]--- @'bifold' ≡ 'bifoldMap' 'id' 'id'@ +-- @'bifold' ≡ 'bifoldMap' 'id' 'id'@ -- [/BifoldMap Identity/] -- @'bifoldMap' f g ≡ 'bifoldr' ('mappend' '.' f) ('mappend' '.' g) 'mempty'@--- [/Bifoldr Identity/] +-- [/Bifoldr Identity/] -- @'bifoldr' f g z t ≡ 'appEndo' ('bifoldMap' ('Endo' '.' f) ('Endo' '.' g) t) z@ -- -- /Note/: This property test is only available when this package is built with@@ -80,7 +80,7 @@ (Bifoldable f, Bifunctor f, Eq2 f, Show2 f, Arbitrary2 f) #endif => proxy f -> Property-bifoldableFunctorLaw _ = property $ \(Apply2 (x :: f Integer Integer)) -> bifoldMap Sum Sum x == (bifold (bimap Sum Sum x))+bifoldableFunctorLaw _ = property $ \(Apply2 (x :: f Integer Integer)) -> bifoldMap mkMonoid mkMonoid x == (bifold (bimap mkMonoid mkMonoid x)) bifoldableFunctorImplication :: forall proxy f. #if HAVE_QUANTIFIED_CONSTRAINTS@@ -89,7 +89,7 @@ (Bifoldable f, Bifunctor f, Eq2 f, Show2 f, Arbitrary2 f) #endif => proxy f -> Property-bifoldableFunctorImplication _ = property $ \(Apply2 (x :: f Integer Integer)) -> bifoldMap Sum Sum (bimap Product Product x) == bifoldMap (Sum . Product) (Sum . Product) x+bifoldableFunctorImplication _ = property $ \(Apply2 (x :: f Integer Integer)) -> bifoldMap mkMonoid mkMonoid (bimap mkMonoid mkMonoid x) == bifoldMap (mkMonoid . mkMonoid) (mkMonoid . mkMonoid) x bifoldIdentity :: forall proxy f. #if HAVE_QUANTIFIED_CONSTRAINTS@@ -98,7 +98,7 @@ (Bifoldable f, Eq2 f, Show2 f, Arbitrary2 f) #endif => proxy f -> Property-bifoldIdentity _ = property $ \(Apply2 (x :: f (Sum Integer) (Sum Integer))) -> (bifold x) == (bifoldMap id id x)+bifoldIdentity _ = property $ \(Apply2 (x :: f [Integer] [Integer])) -> (bifold x) == (bifoldMap id id x) bifoldMapIdentity :: forall proxy f. #if HAVE_QUANTIFIED_CONSTRAINTS@@ -107,7 +107,7 @@ (Bifoldable f, Eq2 f, Show2 f, Arbitrary2 f) #endif => proxy f -> Property-bifoldMapIdentity _ = property $ \(Apply2 (x :: f Integer Integer)) -> bifoldMap Sum Sum x == bifoldr (mappend . Sum) (mappend . Sum) mempty x+bifoldMapIdentity _ = property $ \(Apply2 (x :: f Integer Integer)) -> bifoldMap mkMonoid mkMonoid x == bifoldr (mappend . mkMonoid) (mappend . mkMonoid) mempty x bifoldrIdentity :: forall proxy f. #if HAVE_QUANTIFIED_CONSTRAINTS@@ -119,6 +119,8 @@ bifoldrIdentity _ = property $ \(Apply2 (x :: f Integer Integer)) -> let f _ _ = mempty g _ _ = mempty- in bifoldr f g (mempty :: Sum Integer) x == appEndo (bifoldMap (Endo . f) (Endo . g) x) mempty+ in bifoldr f g (mempty :: [Integer]) x == appEndo (bifoldMap (Endo . f) (Endo . g) x) mempty +mkMonoid :: a -> [a]+mkMonoid x = [x] #endif
src/Test/QuickCheck/Classes/Eq.hs view
@@ -4,22 +4,26 @@ module Test.QuickCheck.Classes.Eq ( eqLaws+ , substitutiveEqLaws ) where import Data.Proxy (Proxy) import Test.QuickCheck hiding ((.&.)) import Test.QuickCheck.Property (Property)+import Test.QuickCheck.Function import Test.QuickCheck.Classes.Internal (Laws(..)) -- | Tests the following properties: -- -- [/Transitive/]--- @a == b ∧ b == c ⇒ a == c@+-- @a '==' b ∧ b '==' c ⇒ a '==' c@ -- [/Symmetric/]--- @a == b ⇒ b == a@+-- @a '==' b ⇒ b '==' a@ -- [/Reflexive/]--- @a == a@+-- @a '==' a@+-- [/Negation/]+-- @x '/=' y '==' 'not' (x '==' y)@ -- -- Some of these properties involve implication. In the case that -- the left hand side of the implication arrow does not hold, we@@ -48,3 +52,21 @@ eqReflexive :: forall a. (Show a, Eq a, Arbitrary a) => Proxy a -> Property eqReflexive _ = property $ \(a :: a) -> a == a++eqNegation :: forall a. (Show a, Eq a, Arbitrary a) => Proxy a -> Property+eqNegation _ = property $ \(x :: a) y -> (x /= y) == not (x == y)++-- | Tests the following properties:+--+-- [/Substitutive/]+-- @x '==' y ⇒ f x '==' f y@+--+-- /Note/: This does not test `eqLaws`.+-- If you want to use this, You should use it in addition to `eqLaws`.+substitutiveEqLaws :: forall a. (Eq a, Arbitrary a, CoArbitrary a, Function a, Show a) => Proxy a -> Laws+substitutiveEqLaws _ = Laws "Eq"+ [ ("Substitutivity"+ , property $ \(x :: a) y (f :: Fun a Integer) ->+ x == y ==> applyFun f x == applyFun f y+ )+ ]
src/Test/QuickCheck/Classes/Integral.hs view
@@ -20,11 +20,17 @@ -- @(div x y) * y + (mod x y) ≡ x@ -- [/Integer Roundtrip/] -- @fromInteger (toInteger x) ≡ x@+-- [/QuotRem is (Quot, Rem)/]+-- @quotRem x y ≡ (quot x y, rem x y)@+-- [/DivMod is (Div, Mod)/]+-- @divMod x y ≡ (div x y, mod x y)@ integralLaws :: (Integral a, Arbitrary a, Show a) => Proxy a -> Laws integralLaws p = Laws "Integral" [ ("Quotient Remainder", integralQuotientRemainder p) , ("Division Modulus", integralDivisionModulus p) , ("Integer Roundtrip", integralIntegerRoundtrip p)+ , ("QuotRem is (Quot, Rem)", integralQuotRem p)+ , ("DivMod is (Div, Mod)", integralDivMod p) ] integralQuotientRemainder :: forall a. (Integral a, Arbitrary a, Show a) => Proxy a -> Property@@ -50,3 +56,19 @@ (\x -> fromInteger (toInteger x)) "x" (\x -> x)++integralQuotRem :: forall a. (Integral a, Arbitrary a, Show a) => Proxy a -> Property+integralQuotRem _ = myForAllShrink False (\(_,y) -> y /= 0)+ (\(x :: a, y) -> ["x = " ++ show x, "y = " ++ show y])+ "quotRem x y"+ (\(x,y) -> quotRem x y)+ "(quot x y, rem x y)"+ (\(x,y) -> (quot x y, rem x y))++integralDivMod :: forall a. (Integral a, Arbitrary a, Show a) => Proxy a -> Property+integralDivMod _ = myForAllShrink False (\(_,y) -> y /= 0)+ (\(x :: a, y) -> ["x = " ++ show x, "y = " ++ show y])+ "divMod x y"+ (\(x,y) -> divMod x y)+ "(div x y, mod x y)"+ (\(x,y) -> (div x y, mod x y))
src/Test/QuickCheck/Classes/Internal.hs view
@@ -15,14 +15,14 @@ -- public API and is not subject to PVP. It is used by other -- modules in @quickcheck-classes-base@ and by modules in the -- @quickcheck-classes@ library as well. Functions and types--- in this module are either auxiliary functions that are reused +-- in this module are either auxiliary functions that are reused -- by many different laws tests, or they are compatibility shims -- that make it possible to build with older versions GHC and -- transformers. module Test.QuickCheck.Classes.Internal ( -- * Common Types and Functions Laws(..)- , foldMapA + , foldMapA , myForAllShrink -- Modifiers , SmallList(..)@@ -120,6 +120,12 @@ #endif -- | A set of laws associated with a typeclass.+--+-- /Note/: Most of the top-level functions provided+-- by this library have the shape+-- `forall a. (Ctx a) => Proxy a -> Laws`. You can just+-- as easily provide your own `Laws` in libraries/test suites+-- using regular QuickCheck machinery. data Laws = Laws { lawsTypeclass :: String -- ^ Name of the typeclass whose laws are tested
src/Test/QuickCheck/Classes/Num.hs view
@@ -38,6 +38,12 @@ -- @'negate' a '+' a ≡ 0@ -- [/Subtraction/] -- @a '+' 'negate' b ≡ a '-' b@+-- [/Abs Is Idempotent/]+-- @'abs' ('abs' a) ≡ 'abs' a+-- [/Signum Is Idempotent/]+-- @'signum' ('signum' a) ≡ 'signum' a+-- [/Product Of Abs And Signum Is Id/]+-- @'abs' a * 'signum' a ≡ a@ numLaws :: (Num a, Eq a, Arbitrary a, Show a) => Proxy a -> Laws numLaws p = Laws "Num" [ ("Additive Commutativity", numCommutativePlus p)@@ -52,6 +58,9 @@ , ("Multiplicative Right Annihilation", numRightAnnihilation p) , ("Additive Inverse", numAdditiveInverse p) , ("Subtraction", numSubtraction p)+ , ("Abs Is Idempotent", absIdempotence p)+ , ("Signum Is Idempotent", signumIdempotence p)+ , ("Product Of Abs And Signum Is Id", absSignumId p) ] numLeftMultiplicationDistributes :: forall a. (Num a, Eq a, Arbitrary a, Show a) => Proxy a -> Property@@ -149,3 +158,27 @@ (\(a,b) -> a + negate b) "a - b" (\(a,b) -> a - b)++absIdempotence :: forall a. (Num a, Eq a, Arbitrary a, Show a) => Proxy a -> Property+absIdempotence _ = myForAllShrink True (const True)+ (\(a :: a) -> ["a = " ++ show a])+ "abs (abs a)"+ (abs . abs)+ "abs a"+ abs++signumIdempotence :: forall a. (Num a, Eq a, Arbitrary a, Show a) => Proxy a -> Property+signumIdempotence _ = myForAllShrink True (const True)+ (\(a :: a) -> ["a = " ++ show a])+ "signum (signum a)"+ (signum . signum)+ "signum a"+ signum++absSignumId :: forall a. (Num a, Eq a, Arbitrary a, Show a) => Proxy a -> Property+absSignumId _ = myForAllShrink True (const True)+ (\(a :: a) -> ["a = " ++ show a])+ "abs a * signum a"+ (\a -> abs a * signum a)+ "a"+ id
src/Test/QuickCheck/Classes/Storable.hs view
@@ -36,6 +36,7 @@ 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)+ , ("Set-Set (if you set something twice, the first set is inconsequential", storableSetSet p) , ("List Conversion Roundtrips", storableList p) , ("peekElemOff a i ≡ peek (plusPtr a (i * sizeOf undefined))", storablePeekElem p) , ("peekElemOff a i x ≡ poke (plusPtr a (i * sizeOf undefined)) x ≡ id ", storablePokeElem p)@@ -125,6 +126,18 @@ free ptrA free ptrB return res++storableSetSet :: forall a. (Storable a, Eq a, Arbitrary a, Show a) => Proxy a -> Property+storableSetSet _ = property $ \(x :: a) (y :: a) (as :: [a]) -> (not (L.null as)) ==> do+ let len = L.length as+ ix <- choose (0,len-1)+ return $ unsafePerformIO $ do+ ptr <- arrayArbitrary len+ pokeElemOff ptr ix x+ pokeElemOff ptr ix y+ atIx <- peekElemOff ptr ix+ free ptr+ return $ atIx == y storableList :: forall a. (Storable a, Eq a, Arbitrary a, Show a) => Proxy a -> Property storableList _ = property $ \(as :: [a]) -> unsafePerformIO $ do