From 9f046e2f42329f1d28eeb4b44bc88697b0f2fcdd Mon Sep 17 00:00:00 2001 From: Jonathan Knowles Date: Sun, 5 Feb 2023 02:39:53 +0000 Subject: [PATCH 01/25] Add module documentation for `Test.QuickCheck.Classes.Group`. --- src/public/Test/QuickCheck/Classes/Group.hs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/public/Test/QuickCheck/Classes/Group.hs b/src/public/Test/QuickCheck/Classes/Group.hs index 9ec8669..eac7dc5 100644 --- a/src/public/Test/QuickCheck/Classes/Group.hs +++ b/src/public/Test/QuickCheck/Classes/Group.hs @@ -5,6 +5,9 @@ -- Copyright: © 2022–2023 Jonathan Knowles -- License: Apache-2.0 -- +-- This module provides 'Laws' definitions for classes exported by +-- "Data.Group". +-- module Test.QuickCheck.Classes.Group ( -- * Group From 11e4d5325001d0c2cec85ffd0fc157fbb41a995d Mon Sep 17 00:00:00 2001 From: Jonathan Knowles Date: Sun, 5 Feb 2023 03:27:15 +0000 Subject: [PATCH 02/25] Add hyperlinks to documentation of laws. --- src/public/Test/QuickCheck/Classes/Group.hs | 61 +++++++++++++++++---- 1 file changed, 50 insertions(+), 11 deletions(-) diff --git a/src/public/Test/QuickCheck/Classes/Group.hs b/src/public/Test/QuickCheck/Classes/Group.hs index eac7dc5..7fbeaff 100644 --- a/src/public/Test/QuickCheck/Classes/Group.hs +++ b/src/public/Test/QuickCheck/Classes/Group.hs @@ -45,16 +45,45 @@ import Test.QuickCheck.Classes.Group.Internal -- | 'Laws' for instances of 'Group'. -- --- Tests the following properties: +-- Includes the following laws: -- --- prop> invert mempty == mempty --- prop> a <> invert a == mempty --- prop> invert a <> a == mempty --- prop> a ~~ mempty == a --- prop> a ~~ a == mempty --- prop> a ~~ b == a <> invert b --- prop> n >= 0 ==> pow a n == mconcat (replicate n a) --- prop> n <= 0 ==> pow a n == invert (mconcat (replicate (abs n) a)) +-- __/Inversion/__ +-- +-- @ +-- 'invert' 'mempty' '==' 'mempty' +-- @ +-- +-- @ +-- \ \ a '<>' 'invert' a '==' 'mempty' +-- 'invert' a '<>' \ \ a '==' 'mempty' +-- @ +-- +-- __/Subtraction/__ +-- +-- @ +-- a '~~' 'mempty' '==' a +-- @ +-- +-- @ +-- a '~~' a '==' 'mempty' +-- @ +-- +-- @ +-- a '~~' b '==' a '<>' 'invert' b +-- @ +-- +-- __/Exponentiation/__ +-- +-- @ +-- n '>=' 0 ==> 'pow' a n '==' \ \ 'mconcat' ('replicate' \ \ n a) +-- n '<=' 0 ==> 'pow' a n '==' 'invert' ('mconcat' ('replicate' ('abs' n) a)) +-- @ +-- +-- == Superclass laws +-- +-- Note that the following superclass laws are __not__ included: +-- +-- * 'Test.QuickCheck.Classes.monoidLaws' -- groupLaws :: forall a. (Arbitrary a, Show a, Eq a, Group a) @@ -151,9 +180,19 @@ groupLaw_pow_nonPositive a = -- | 'Laws' for instances of 'Abelian'. -- --- Tests the following property: +-- Includes the following law: +-- +-- __/Commutativity/__ +-- +-- @ +-- a '<>' b '==' b '<>' a +-- @ +-- +-- == Superclass laws +-- +-- Note that the following superclass laws are __not__ included: -- --- prop> a <> b == b <> a +-- * 'Test.QuickCheck.Classes.Group.groupLaws' -- abelianLaws :: forall a. (Arbitrary a, Show a, Eq a, Abelian a) From a0d041b06819e3f1d54d752e725f3860b3220b84 Mon Sep 17 00:00:00 2001 From: Jonathan Knowles Date: Sun, 5 Feb 2023 03:31:29 +0000 Subject: [PATCH 03/25] Relocate module `Internal`. --- quickcheck-groups.cabal | 2 +- src/internal/{Test/QuickCheck/Classes/Group => }/Internal.hs | 2 +- src/public/Test/QuickCheck/Classes/Group.hs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) rename src/internal/{Test/QuickCheck/Classes/Group => }/Internal.hs (98%) diff --git a/quickcheck-groups.cabal b/quickcheck-groups.cabal index 9d981bc..6aeb45f 100644 --- a/quickcheck-groups.cabal +++ b/quickcheck-groups.cabal @@ -60,7 +60,7 @@ library internal src/internal exposed-modules: Data.Semigroup.Eq - Test.QuickCheck.Classes.Group.Internal + Internal Test.QuickCheck.Classes.Group.Tuple default-language: Haskell2010 diff --git a/src/internal/Test/QuickCheck/Classes/Group/Internal.hs b/src/internal/Internal.hs similarity index 98% rename from src/internal/Test/QuickCheck/Classes/Group/Internal.hs rename to src/internal/Internal.hs index cdab22b..578f2b8 100644 --- a/src/internal/Test/QuickCheck/Classes/Group/Internal.hs +++ b/src/internal/Internal.hs @@ -2,7 +2,7 @@ -- Copyright: © 2022–2023 Jonathan Knowles -- License: Apache-2.0 -- -module Test.QuickCheck.Classes.Group.Internal +module Internal ( makeLaw0 , makeLaw1 , makeLaw2 diff --git a/src/public/Test/QuickCheck/Classes/Group.hs b/src/public/Test/QuickCheck/Classes/Group.hs index 7fbeaff..78ae778 100644 --- a/src/public/Test/QuickCheck/Classes/Group.hs +++ b/src/public/Test/QuickCheck/Classes/Group.hs @@ -26,6 +26,8 @@ import Data.Group ( Abelian, Group (..) ) import Data.Proxy ( Proxy (..) ) +import Internal + ( makeLaw0, makeLaw1, makeLaw2, makeProperty ) import Test.QuickCheck ( Arbitrary (..) , NonNegative (..) @@ -36,8 +38,6 @@ import Test.QuickCheck ) import Test.QuickCheck.Classes ( Laws (..) ) -import Test.QuickCheck.Classes.Group.Internal - ( makeLaw0, makeLaw1, makeLaw2, makeProperty ) -------------------------------------------------------------------------------- -- Group From 592667de6a281b286ccfdc556a60dca20dc503a3 Mon Sep 17 00:00:00 2001 From: Jonathan Knowles Date: Sun, 5 Feb 2023 03:36:04 +0000 Subject: [PATCH 04/25] Relocate module `Semigroup.Eq`. --- quickcheck-groups.cabal | 2 +- src/internal/Internal.hs | 2 +- src/internal/{Data => Internal}/Semigroup/Eq.hs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename src/internal/{Data => Internal}/Semigroup/Eq.hs (96%) diff --git a/quickcheck-groups.cabal b/quickcheck-groups.cabal index 6aeb45f..6a4b3bc 100644 --- a/quickcheck-groups.cabal +++ b/quickcheck-groups.cabal @@ -59,8 +59,8 @@ library internal hs-source-dirs: src/internal exposed-modules: - Data.Semigroup.Eq Internal + Internal.Semigroup.Eq Test.QuickCheck.Classes.Group.Tuple default-language: Haskell2010 diff --git a/src/internal/Internal.hs b/src/internal/Internal.hs index 578f2b8..13eec72 100644 --- a/src/internal/Internal.hs +++ b/src/internal/Internal.hs @@ -15,7 +15,7 @@ import Data.Function ( (&) ) import Data.Proxy ( Proxy (..) ) -import Data.Semigroup.Eq +import Internal.Semigroup.Eq ( allUnique, canVerifyAllNonNull ) import Test.QuickCheck ( Arbitrary (..) diff --git a/src/internal/Data/Semigroup/Eq.hs b/src/internal/Internal/Semigroup/Eq.hs similarity index 96% rename from src/internal/Data/Semigroup/Eq.hs rename to src/internal/Internal/Semigroup/Eq.hs index f9b520c..285bb33 100644 --- a/src/internal/Data/Semigroup/Eq.hs +++ b/src/internal/Internal/Semigroup/Eq.hs @@ -5,7 +5,7 @@ -- Copyright: © 2022–2023 Jonathan Knowles -- License: Apache-2.0 -- -module Data.Semigroup.Eq +module Internal.Semigroup.Eq ( allUnique , canVerifyAllNonNull ) From 0f69520089e711e0d385ed6eb2075e4adfb7c262 Mon Sep 17 00:00:00 2001 From: Jonathan Knowles Date: Sun, 5 Feb 2023 03:40:01 +0000 Subject: [PATCH 05/25] Relocate module `Semigroup.Tuple`. --- quickcheck-groups.cabal | 2 +- src/internal/Internal.hs | 4 ++-- .../QuickCheck/Classes/Group => Internal/Semigroup}/Tuple.hs | 2 +- src/prelude/Test/QuickCheck/Classes/Group/Prelude.hs | 2 -- 4 files changed, 4 insertions(+), 6 deletions(-) rename src/internal/{Test/QuickCheck/Classes/Group => Internal/Semigroup}/Tuple.hs (99%) diff --git a/quickcheck-groups.cabal b/quickcheck-groups.cabal index 6a4b3bc..3830920 100644 --- a/quickcheck-groups.cabal +++ b/quickcheck-groups.cabal @@ -61,7 +61,7 @@ library internal exposed-modules: Internal Internal.Semigroup.Eq - Test.QuickCheck.Classes.Group.Tuple + Internal.Semigroup.Tuple default-language: Haskell2010 build-depends: diff --git a/src/internal/Internal.hs b/src/internal/Internal.hs index 13eec72..f2f72fe 100644 --- a/src/internal/Internal.hs +++ b/src/internal/Internal.hs @@ -17,6 +17,8 @@ import Data.Proxy ( Proxy (..) ) import Internal.Semigroup.Eq ( allUnique, canVerifyAllNonNull ) +import Internal.Semigroup.Tuple + ( Tuple1, Tuple2, Tuple3, evalTuple1, evalTuple2, evalTuple3 ) import Test.QuickCheck ( Arbitrary (..) , Property @@ -26,8 +28,6 @@ import Test.QuickCheck , cover , property ) -import Test.QuickCheck.Classes.Group.Tuple - ( Tuple1, Tuple2, Tuple3, evalTuple1, evalTuple2, evalTuple3 ) makeLaw :: Testable t => String -> t -> (String, Property) makeLaw title t = (title, checkCoverage $ property t) diff --git a/src/internal/Test/QuickCheck/Classes/Group/Tuple.hs b/src/internal/Internal/Semigroup/Tuple.hs similarity index 99% rename from src/internal/Test/QuickCheck/Classes/Group/Tuple.hs rename to src/internal/Internal/Semigroup/Tuple.hs index 6caf9f2..e7ac969 100644 --- a/src/internal/Test/QuickCheck/Classes/Group/Tuple.hs +++ b/src/internal/Internal/Semigroup/Tuple.hs @@ -4,7 +4,7 @@ -- Copyright: © 2022–2023 Jonathan Knowles -- License: Apache-2.0 -- -module Test.QuickCheck.Classes.Group.Tuple +module Internal.Semigroup.Tuple where import Data.List.NonEmpty diff --git a/src/prelude/Test/QuickCheck/Classes/Group/Prelude.hs b/src/prelude/Test/QuickCheck/Classes/Group/Prelude.hs index 053cb96..c0321a8 100644 --- a/src/prelude/Test/QuickCheck/Classes/Group/Prelude.hs +++ b/src/prelude/Test/QuickCheck/Classes/Group/Prelude.hs @@ -7,7 +7,6 @@ module Test.QuickCheck.Classes.Group.Prelude , module Numeric.Natural , module Test.QuickCheck , module Test.QuickCheck.Classes.Group - , module Test.QuickCheck.Classes.Group.Tuple ) where @@ -15,6 +14,5 @@ import Data.Group import Numeric.Natural import Test.QuickCheck import Test.QuickCheck.Classes.Group -import Test.QuickCheck.Classes.Group.Tuple import Test.QuickCheck.Instances.Natural () From 48716866239d002ef6f26831ed886435a236674d Mon Sep 17 00:00:00 2001 From: Jonathan Knowles Date: Sun, 5 Feb 2023 03:43:10 +0000 Subject: [PATCH 06/25] Miscellaneous improvements to `internal` library. The `internal` library within `quickcheck-groups` was originally a fork of the `internal` library within `quickcheck-monoid-subclasses`. This commit brings the `internal` library up to date with recent changes made to the `quickcheck-monoid-subclasses' library. In future, we should consider extracting out this `internal` library to a separate library on hackage, to avoid code duplication. --- src/internal/Internal.hs | 72 ++++++++++++++++----------- src/internal/Internal/Semigroup/Eq.hs | 17 ++++--- 2 files changed, 52 insertions(+), 37 deletions(-) diff --git a/src/internal/Internal.hs b/src/internal/Internal.hs index f2f72fe..63b5e5b 100644 --- a/src/internal/Internal.hs +++ b/src/internal/Internal.hs @@ -3,11 +3,14 @@ -- License: Apache-2.0 -- module Internal - ( makeLaw0 + ( cover + , makeLaw0 , makeLaw1 , makeLaw2 , makeLaw3 , makeProperty + , report + , (==>) ) where @@ -16,7 +19,7 @@ import Data.Function import Data.Proxy ( Proxy (..) ) import Internal.Semigroup.Eq - ( allUnique, canVerifyAllNonNull ) + ( allNonNull, allUnique, allUniqueNonNull ) import Internal.Semigroup.Tuple ( Tuple1, Tuple2, Tuple3, evalTuple1, evalTuple2, evalTuple3 ) import Test.QuickCheck @@ -25,22 +28,29 @@ import Test.QuickCheck , Testable , checkCoverage , counterexample - , cover , property ) +import qualified Test.QuickCheck as QC + +infixr 0 ==> +(==>) :: Bool -> Bool -> Bool +a ==> b = not a || b + +cover :: Testable t => String -> Bool -> t -> Property +cover name = flip (QC.cover 0.1) (replaceSpecialChars <$> name) + makeLaw :: Testable t => String -> t -> (String, Property) makeLaw title t = (title, checkCoverage $ property t) makeLaw0 - :: forall a. (Eq a, Monoid a) - => String + :: String -> (Proxy a -> Property) -> (String, Property) makeLaw0 s = makeLaw s . makeProperty0 makeLaw1 - :: (Arbitrary a, Show a, Eq a, Monoid a, Testable t) + :: (Arbitrary a, Show a, Eq a, Semigroup a, Testable t) => String -> (a -> t) -> (String, Property) @@ -70,9 +80,6 @@ makeProperty propertyDescription t = & fmap replaceSpecialChars ] where - replaceSpecialChars = \case - 'λ' -> '\\' - other -> other makeProperty0 :: forall a t. Testable t @@ -81,28 +88,26 @@ makeProperty0 makeProperty0 p = property $ p $ Proxy @a makeProperty1 - :: (Eq a, Monoid a, Testable t) + :: (Eq a, Semigroup a, Testable t) => (a -> t) -> (Tuple1 a -> Property) makeProperty1 p (evalTuple1 -> a) - = cover 1 (a == mempty) "a == mempty" - $ cover 1 (a /= mempty) "a /= mempty" - $ property $ p a + = property $ p a makeProperty2 :: (Eq a, Semigroup a, Testable t) => (a -> a -> t) -> (Tuple2 a -> Property) makeProperty2 p (evalTuple2 -> (a, b)) - = cover 1 - (allUnique [a, b]) + = cover "allUnique [a, b]" - $ cover 1 - (canVerifyAllNonNull [a, b]) - "canVerifyAllNonNull [a, b]" - $ cover 1 - (allUnique [a, b] && canVerifyAllNonNull [a, b]) - "allUnique [a, b] && canVerifyAllNonNull [a, b]" + (allUnique [a, b]) + $ cover + "allNonNull [a, b]" + (allNonNull [a, b]) + $ cover + "allUniqueNonNull [a, b]" + (allUniqueNonNull [a, b]) $ property $ p a b makeProperty3 @@ -110,13 +115,22 @@ makeProperty3 => (a -> a -> a -> t) -> (Tuple3 a -> Property) makeProperty3 p (evalTuple3 -> (a, b, c)) - = cover 1 - (allUnique [a, b, c]) + = cover "allUnique [a, b, c]" - $ cover 1 - (canVerifyAllNonNull [a, b, c]) - "canVerifyAllNonNull [a, b, c]" - $ cover 1 - (allUnique [a, b, c] && canVerifyAllNonNull [a, b, c]) - "allUnique [a, b, c] && canVerifyAllNonNull [a, b, c]" + (allUnique [a, b, c]) + $ cover + "allNonNull [a, b, c]" + (allNonNull [a, b, c]) + $ cover + "allUniqueNonNull [a, b, c]" + (allUniqueNonNull [a, b, c]) $ property $ p a b c + +report :: (Show a, Testable prop) => String -> a -> prop -> Property +report name a = counterexample $ + (replaceSpecialChars <$> name) <> ":\n" <> show a <> "\n" + +replaceSpecialChars :: Char -> Char +replaceSpecialChars = \case + 'λ' -> '\\' + other -> other diff --git a/src/internal/Internal/Semigroup/Eq.hs b/src/internal/Internal/Semigroup/Eq.hs index 285bb33..c57d135 100644 --- a/src/internal/Internal/Semigroup/Eq.hs +++ b/src/internal/Internal/Semigroup/Eq.hs @@ -1,13 +1,11 @@ -{-# LANGUAGE ConstraintKinds #-} -{-# LANGUAGE DataKinds #-} - -- | -- Copyright: © 2022–2023 Jonathan Knowles -- License: Apache-2.0 -- module Internal.Semigroup.Eq - ( allUnique - , canVerifyAllNonNull + ( allNonNull + , allUnique + , allUniqueNonNull ) where @@ -16,11 +14,17 @@ import Data.List import Data.Foldable as F +allNonNull :: (Eq a, Semigroup a, Foldable f) => f a -> Bool +allNonNull as = F.all (as `canVerifyNonNull`) as + allUnique :: (Eq a, Foldable f) => f a -> Bool allUnique as = length (nub xs) == length xs where xs = F.toList as +allUniqueNonNull :: (Eq a, Foldable f, Semigroup a) => f a -> Bool +allUniqueNonNull as = allUnique as && allNonNull as + canModify :: (Eq a, Semigroup a) => a -> a -> Bool a `canModify` b = (||) (a `canModifyL` b) @@ -32,8 +36,5 @@ a `canModifyL` b = b /= a <> b canModifyR :: (Eq a, Semigroup a) => a -> a -> Bool a `canModifyR` b = b /= b <> a -canVerifyAllNonNull :: (Eq a, Semigroup a, Foldable f) => f a -> Bool -canVerifyAllNonNull as = F.all (as `canVerifyNonNull`) as - canVerifyNonNull :: (Eq a, Semigroup a, Foldable f) => f a -> a -> Bool canVerifyNonNull as a = F.any (a `canModify`) as From fc4ff1a9555c6d2b8ce8e14f15af772631375164 Mon Sep 17 00:00:00 2001 From: Jonathan Knowles Date: Sun, 5 Feb 2023 04:00:57 +0000 Subject: [PATCH 07/25] Improve reporting of counterexamples. This commit adds reporting of sub-expression values to counterexamples. --- src/public/Test/QuickCheck/Classes/Group.hs | 88 ++++++++++++++++++--- 1 file changed, 77 insertions(+), 11 deletions(-) diff --git a/src/public/Test/QuickCheck/Classes/Group.hs b/src/public/Test/QuickCheck/Classes/Group.hs index 78ae778..7a78230 100644 --- a/src/public/Test/QuickCheck/Classes/Group.hs +++ b/src/public/Test/QuickCheck/Classes/Group.hs @@ -27,7 +27,7 @@ import Data.Group import Data.Proxy ( Proxy (..) ) import Internal - ( makeLaw0, makeLaw1, makeLaw2, makeProperty ) + ( makeLaw0, makeLaw1, makeLaw2, makeProperty, report ) import Test.QuickCheck ( Arbitrary (..) , NonNegative (..) @@ -117,62 +117,122 @@ groupLaws _ = Laws "Group" ] groupLaw_invert_mempty - :: forall a. (Eq a, Group a) => Proxy a -> Property + :: forall a. (Eq a, Show a, Group a) => Proxy a -> Property groupLaw_invert_mempty _ = makeProperty "invert (mempty @a) == (mempty @a)" (invert (mempty @a) == (mempty @a)) + & report + "mempty @a" + (mempty @a) + & report + "invert (mempty @a)" + (invert (mempty @a)) groupLaw_invert_mappend_1 - :: (Eq a, Group a) => a -> Property + :: forall a. (Eq a, Show a, Group a) => a -> Property groupLaw_invert_mappend_1 a = makeProperty "a <> invert a == mempty" (a <> invert a == mempty) + & report + "mempty @a" + (mempty @a) + & report + "invert a" + (invert a) + & report + "a <> invert a" + (a <> invert a) groupLaw_invert_mappend_2 - :: (Eq a, Group a) => a -> Property + :: forall a. (Eq a, Show a, Group a) => a -> Property groupLaw_invert_mappend_2 a = makeProperty "invert a <> a == mempty" (invert a <> a == mempty) + & report + "mempty @a" + (mempty @a) + & report + "invert a" + (invert a) + & report + "invert a <> a" + (invert a <> a) groupLaw_subtract_mempty - :: (Eq a, Group a) => a -> Property + :: forall a. (Eq a, Show a, Group a) => a -> Property groupLaw_subtract_mempty a = makeProperty "a ~~ mempty == a" (a ~~ mempty == a) + & report + "mempty @a" + (mempty @a) + & report + "a ~~ mempty" + (a ~~ mempty) groupLaw_subtract_self - :: (Eq a, Group a) => a -> Property + :: forall a. (Eq a, Show a, Group a) => a -> Property groupLaw_subtract_self a = makeProperty "a ~~ a == mempty" - (a ~~ a == mempty) + (a ~~ a == mempty @a) + & report + "mempty @a" + (mempty @a) + & report + "a ~~ a" + (a ~~ a) groupLaw_subtract_other - :: (Eq a, Group a) => a -> a -> Property + :: (Eq a, Show a, Group a) => a -> a -> Property groupLaw_subtract_other a b = makeProperty "a ~~ b == a <> invert b" (a ~~ b == a <> invert b) + & report + "a ~~ b" + (a ~~ b) + & report + "invert b" + (invert b) + & report + "a <> invert b" + (a <> invert b) groupLaw_pow_nonNegative - :: (Eq a, Group a) => a -> Property + :: (Eq a, Show a, Group a) => a -> Property groupLaw_pow_nonNegative a = forAllShrink (arbitrary @(NonNegative Int)) shrink $ \(NonNegative n) -> makeProperty "pow a n == mconcat (replicate n a)" (pow a n == mconcat (replicate n a)) + & report + "pow a n" + (pow a n) + & report + "mconcat (replicate n a)" + (mconcat (replicate n a)) groupLaw_pow_nonPositive - :: (Eq a, Group a) => a -> Property + :: (Eq a, Show a, Group a) => a -> Property groupLaw_pow_nonPositive a = forAllShrink (arbitrary @(NonPositive Int)) shrink $ \(NonPositive n) -> makeProperty "pow a n == invert (mconcat (replicate (abs n) a))" (pow a n == invert (mconcat (replicate (abs n) a))) + & report + "pow a n" + (pow a n) + & report + "mconcat (replicate (abs n) a)" + (mconcat (replicate (abs n) a)) + & report + "invert (mconcat (replicate (abs n) a))" + (invert (mconcat (replicate (abs n) a))) -------------------------------------------------------------------------------- -- Abelian @@ -205,11 +265,17 @@ abelianLaws _ = Laws "Abelian" ] abelianLaw_commutative - :: (Eq a, Abelian a) => a -> a -> Property + :: (Eq a, Show a, Abelian a) => a -> a -> Property abelianLaw_commutative a b = makeProperty "a <> b == b <> a" (a <> b == b <> a) + & report + "a <> b" + (a <> b) + & report + "b <> a" + (b <> a) & cover 1 ((a /= b) && (a <> b /= a) && (b <> a /= b)) "(a /= b) && (a <> b /= a) && (b <> a /= b)" From e32994a3374c81e7c37e03c73c8da37a774c5b2d Mon Sep 17 00:00:00 2001 From: Jonathan Knowles Date: Sun, 5 Feb 2023 04:02:32 +0000 Subject: [PATCH 08/25] Use internal `cover` function instead of standard QC function. --- src/public/Test/QuickCheck/Classes/Group.hs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/public/Test/QuickCheck/Classes/Group.hs b/src/public/Test/QuickCheck/Classes/Group.hs index 7a78230..7e6efc2 100644 --- a/src/public/Test/QuickCheck/Classes/Group.hs +++ b/src/public/Test/QuickCheck/Classes/Group.hs @@ -27,13 +27,12 @@ import Data.Group import Data.Proxy ( Proxy (..) ) import Internal - ( makeLaw0, makeLaw1, makeLaw2, makeProperty, report ) + ( cover, makeLaw0, makeLaw1, makeLaw2, makeProperty, report ) import Test.QuickCheck ( Arbitrary (..) , NonNegative (..) , NonPositive (..) , Property - , cover , forAllShrink ) import Test.QuickCheck.Classes @@ -276,6 +275,6 @@ abelianLaw_commutative a b = & report "b <> a" (b <> a) - & cover 1 - ((a /= b) && (a <> b /= a) && (b <> a /= b)) + & cover "(a /= b) && (a <> b /= a) && (b <> a /= b)" + ((a /= b) && (a <> b /= a) && (b <> a /= b)) From 4444c589e710ce4e24b2ef2c3c5568bf680f24e4 Mon Sep 17 00:00:00 2001 From: Jonathan Knowles Date: Sun, 5 Feb 2023 04:12:02 +0000 Subject: [PATCH 09/25] Add test suite coverage for the `Sum Integer` type. --- src/test/Test/QuickCheck/Classes/GroupSpec.hs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/test/Test/QuickCheck/Classes/GroupSpec.hs b/src/test/Test/QuickCheck/Classes/GroupSpec.hs index 658c4bb..3b35668 100644 --- a/src/test/Test/QuickCheck/Classes/GroupSpec.hs +++ b/src/test/Test/QuickCheck/Classes/GroupSpec.hs @@ -38,6 +38,10 @@ spec = do [ abelianLaws , groupLaws ] + testLawsMany @(Sum TestInteger) + [ abelianLaws + , groupLaws + ] testLawsMany @(Product TestRational) [ abelianLaws , groupLaws @@ -51,6 +55,14 @@ spec = do -- Test types -------------------------------------------------------------------------------- +newtype TestInteger = TestInteger Integer + deriving stock (Eq, Show) + deriving newtype Num + +instance Arbitrary TestInteger where + arbitrary = TestInteger <$> choose (-4, 4) + shrink (TestInteger i) = TestInteger <$> shrink i + newtype TestRational = TestRational Rational deriving stock (Eq, Show) deriving newtype (Num, Fractional) From 7216737c9768499236a22f9efa5d5d9b0e4d8f91 Mon Sep 17 00:00:00 2001 From: Jonathan Knowles Date: Sun, 5 Feb 2023 04:18:12 +0000 Subject: [PATCH 10/25] Add new law definition `groupLaw_pow_zero`. --- src/public/Test/QuickCheck/Classes/Group.hs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/public/Test/QuickCheck/Classes/Group.hs b/src/public/Test/QuickCheck/Classes/Group.hs index 7e6efc2..9d6a073 100644 --- a/src/public/Test/QuickCheck/Classes/Group.hs +++ b/src/public/Test/QuickCheck/Classes/Group.hs @@ -74,6 +74,10 @@ import Test.QuickCheck.Classes -- __/Exponentiation/__ -- -- @ +-- 'pow' a 0 '==' 'mempty' +-- @ +-- +-- @ -- n '>=' 0 ==> 'pow' a n '==' \ \ 'mconcat' ('replicate' \ \ n a) -- n '<=' 0 ==> 'pow' a n '==' 'invert' ('mconcat' ('replicate' ('abs' n) a)) -- @ @@ -107,6 +111,9 @@ groupLaws _ = Laws "Group" , makeLaw2 @a "groupLaw_subtract_other" (groupLaw_subtract_other) + , makeLaw1 @a + "groupLaw_pow_zero" + (groupLaw_pow_zero) , makeLaw1 @a "groupLaw_pow_nonNegative" (groupLaw_pow_nonNegative) @@ -202,6 +209,19 @@ groupLaw_subtract_other a b = "a <> invert b" (a <> invert b) +groupLaw_pow_zero + :: forall a. (Eq a, Show a, Group a) => a -> Property +groupLaw_pow_zero a = + makeProperty + "pow a 0 == mempty" + (pow a 0 == mempty) + & report + "pow a 0" + (pow a 0) + & report + "mempty @a" + (mempty @a) + groupLaw_pow_nonNegative :: (Eq a, Show a, Group a) => a -> Property groupLaw_pow_nonNegative a = From c75b45542a449d6f409a5be8eeb82dd0888cc825 Mon Sep 17 00:00:00 2001 From: Jonathan Knowles Date: Sun, 5 Feb 2023 04:31:49 +0000 Subject: [PATCH 11/25] Add new law definition `groupLaw_invert_invert`. --- src/public/Test/QuickCheck/Classes/Group.hs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/public/Test/QuickCheck/Classes/Group.hs b/src/public/Test/QuickCheck/Classes/Group.hs index 9d6a073..7d9f36c 100644 --- a/src/public/Test/QuickCheck/Classes/Group.hs +++ b/src/public/Test/QuickCheck/Classes/Group.hs @@ -53,6 +53,10 @@ import Test.QuickCheck.Classes -- @ -- -- @ +-- 'invert' ('invert' a) '==' a +-- @ +-- +-- @ -- \ \ a '<>' 'invert' a '==' 'mempty' -- 'invert' a '<>' \ \ a '==' 'mempty' -- @ @@ -96,6 +100,9 @@ groupLaws _ = Laws "Group" [ makeLaw0 @a "groupLaw_invert_mempty" (groupLaw_invert_mempty) + , makeLaw1 @a + "groupLaw_invert_invert" + (groupLaw_invert_invert) , makeLaw1 @a "groupLaw_invert_mappend_1" (groupLaw_invert_mappend_1) @@ -135,6 +142,19 @@ groupLaw_invert_mempty _ = "invert (mempty @a)" (invert (mempty @a)) +groupLaw_invert_invert + :: forall a. (Eq a, Show a, Group a) => a -> Property +groupLaw_invert_invert a = + makeProperty + "invert (invert a) == a" + (invert (invert a) == a) + & report + "invert a" + (invert a) + & report + "invert (invert a)" + (invert (invert a)) + groupLaw_invert_mappend_1 :: forall a. (Eq a, Show a, Group a) => a -> Property groupLaw_invert_mappend_1 a = From eb7da19c094fd3036b7884ebe60f99ce8a25c7ba Mon Sep 17 00:00:00 2001 From: Jonathan Knowles Date: Sun, 5 Feb 2023 04:22:00 +0000 Subject: [PATCH 12/25] Add further coverage checks for exponentiation laws. --- src/public/Test/QuickCheck/Classes/Group.hs | 30 +++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/public/Test/QuickCheck/Classes/Group.hs b/src/public/Test/QuickCheck/Classes/Group.hs index 7d9f36c..bd5d4bd 100644 --- a/src/public/Test/QuickCheck/Classes/Group.hs +++ b/src/public/Test/QuickCheck/Classes/Group.hs @@ -255,6 +255,21 @@ groupLaw_pow_nonNegative a = & report "mconcat (replicate n a)" (mconcat (replicate n a)) + & cover + "n == 0" + (n == 0) + & cover + "n == 1" + (n == 1) + & cover + "n == 2" + (n == 2) + & cover + "n == 3" + (n == 3) + & cover + "n >= 4" + (n >= 4) groupLaw_pow_nonPositive :: (Eq a, Show a, Group a) => a -> Property @@ -272,6 +287,21 @@ groupLaw_pow_nonPositive a = & report "invert (mconcat (replicate (abs n) a))" (invert (mconcat (replicate (abs n) a))) + & cover + "n == -0" + (n == -0) + & cover + "n == -1" + (n == -1) + & cover + "n == -2" + (n == -2) + & cover + "n == -3" + (n == -3) + & cover + "n <= -4" + (n <= -4) -------------------------------------------------------------------------------- -- Abelian From 7b8bc054f4e1291ce0ea89feed550494eff51a07 Mon Sep 17 00:00:00 2001 From: Jonathan Knowles Date: Sun, 5 Feb 2023 04:51:08 +0000 Subject: [PATCH 13/25] Rename module to `Test.Hspec.Laws`. --- quickcheck-groups.cabal | 2 +- src/test/Test/{QuickCheck/Classes/Hspec.hs => Hspec/Laws.hs} | 2 +- src/test/Test/QuickCheck/Classes/GroupSpec.hs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) rename src/test/Test/{QuickCheck/Classes/Hspec.hs => Hspec/Laws.hs} (97%) diff --git a/quickcheck-groups.cabal b/quickcheck-groups.cabal index 3830920..a2245a2 100644 --- a/quickcheck-groups.cabal +++ b/quickcheck-groups.cabal @@ -96,7 +96,7 @@ test-suite test hs-source-dirs: src/test other-modules: - Test.QuickCheck.Classes.Hspec + Test.Hspec.Laws Test.QuickCheck.Classes.GroupSpec type: exitcode-stdio-1.0 default-language: diff --git a/src/test/Test/QuickCheck/Classes/Hspec.hs b/src/test/Test/Hspec/Laws.hs similarity index 97% rename from src/test/Test/QuickCheck/Classes/Hspec.hs rename to src/test/Test/Hspec/Laws.hs index 38f4f3d..3bb2dcf 100644 --- a/src/test/Test/QuickCheck/Classes/Hspec.hs +++ b/src/test/Test/Hspec/Laws.hs @@ -6,7 +6,7 @@ -- -- Provides testing functions to check that type class instances obey laws. -- -module Test.QuickCheck.Classes.Hspec +module Test.Hspec.Laws ( testLaws , testLawsMany ) where diff --git a/src/test/Test/QuickCheck/Classes/GroupSpec.hs b/src/test/Test/QuickCheck/Classes/GroupSpec.hs index 3b35668..dba0a66 100644 --- a/src/test/Test/QuickCheck/Classes/GroupSpec.hs +++ b/src/test/Test/QuickCheck/Classes/GroupSpec.hs @@ -15,14 +15,14 @@ import Data.Ratio ( denominator, numerator, (%) ) import Test.Hspec ( Spec ) +import Test.Hspec.Laws + ( testLawsMany ) import Test.QuickCheck ( Arbitrary (..), NonZero (..), Property, choose, oneof ) import Test.QuickCheck.Classes ( Laws (..) ) import Test.QuickCheck.Classes.Group ( abelianLaws, groupLaws ) -import Test.QuickCheck.Classes.Hspec - ( testLawsMany ) import Test.QuickCheck.Instances.ByteString () import Test.QuickCheck.Instances.Natural From 2a1f89e25d34c99ad377322b545bb374a6b66ca6 Mon Sep 17 00:00:00 2001 From: Jonathan Knowles Date: Sun, 5 Feb 2023 04:52:28 +0000 Subject: [PATCH 14/25] Remove redundant language extension from `Laws` module. --- src/test/Test/Hspec/Laws.hs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/test/Test/Hspec/Laws.hs b/src/test/Test/Hspec/Laws.hs index 3bb2dcf..c6012b8 100644 --- a/src/test/Test/Hspec/Laws.hs +++ b/src/test/Test/Hspec/Laws.hs @@ -1,5 +1,3 @@ -{-# LANGUAGE PolyKinds #-} - -- | -- Copyright: © 2022–2023 Jonathan Knowles -- License: Apache-2.0 From 046b381f94c7d7bddc6ed4559e091e8bbbe4d460 Mon Sep 17 00:00:00 2001 From: Jonathan Knowles Date: Sun, 5 Feb 2023 04:54:53 +0000 Subject: [PATCH 15/25] Rename main test suite module to `ClassSpec`. --- quickcheck-groups.cabal | 2 +- src/test/{Test/QuickCheck/Classes/GroupSpec.hs => ClassSpec.hs} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/test/{Test/QuickCheck/Classes/GroupSpec.hs => ClassSpec.hs} (98%) diff --git a/quickcheck-groups.cabal b/quickcheck-groups.cabal index a2245a2..6316752 100644 --- a/quickcheck-groups.cabal +++ b/quickcheck-groups.cabal @@ -96,8 +96,8 @@ test-suite test hs-source-dirs: src/test other-modules: + ClassSpec Test.Hspec.Laws - Test.QuickCheck.Classes.GroupSpec type: exitcode-stdio-1.0 default-language: Haskell2010 diff --git a/src/test/Test/QuickCheck/Classes/GroupSpec.hs b/src/test/ClassSpec.hs similarity index 98% rename from src/test/Test/QuickCheck/Classes/GroupSpec.hs rename to src/test/ClassSpec.hs index dba0a66..4bfc9e5 100644 --- a/src/test/Test/QuickCheck/Classes/GroupSpec.hs +++ b/src/test/ClassSpec.hs @@ -5,7 +5,7 @@ -- Copyright: © 2022–2023 Jonathan Knowles -- License: Apache-2.0 -- -module Test.QuickCheck.Classes.GroupSpec where +module ClassSpec where import Data.Bifunctor ( bimap ) From efe8e9f15a87cef52e426d2710e8e86dde275092 Mon Sep 17 00:00:00 2001 From: Jonathan Knowles Date: Sun, 5 Feb 2023 04:57:13 +0000 Subject: [PATCH 16/25] Simplify package description and synopsis. --- quickcheck-groups.cabal | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/quickcheck-groups.cabal b/quickcheck-groups.cabal index 6316752..4245469 100644 --- a/quickcheck-groups.cabal +++ b/quickcheck-groups.cabal @@ -8,15 +8,11 @@ author: Jonathan Knowles maintainer: mail@jonathanknowles.net copyright: 2022–2023 Jonathan Knowles category: Testing -synopsis: QuickCheck support for testing instances of classes defined in - the groups library. +synopsis: Testing group class instances with QuickCheck description: - This library provides: - - * QuickCheck support for testing instances of classes defined in the - 'groups' library. - * Reusable properties in the form of 'Laws' definitions. + QuickCheck support for testing instances of type classes defined in the + groups library. extra-source-files: README.md From 6cbf21c3701f89b89367e47a2c3dd45ec42cfd94 Mon Sep 17 00:00:00 2001 From: Jonathan Knowles Date: Sun, 5 Feb 2023 04:59:12 +0000 Subject: [PATCH 17/25] Rename `common-extensions` to `extensions` in cabal file. --- quickcheck-groups.cabal | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/quickcheck-groups.cabal b/quickcheck-groups.cabal index 4245469..79e81ed 100644 --- a/quickcheck-groups.cabal +++ b/quickcheck-groups.cabal @@ -17,7 +17,7 @@ description: extra-source-files: README.md -common common-extensions +common extensions default-extensions: DerivingStrategies FlexibleContexts @@ -35,7 +35,7 @@ source-repository head library import: - common-extensions + extensions hs-source-dirs: src/public exposed-modules: @@ -51,7 +51,7 @@ library library internal import: - common-extensions + extensions hs-source-dirs: src/internal exposed-modules: @@ -68,7 +68,7 @@ library internal library prelude import: - common-extensions + extensions hs-source-dirs: src/prelude exposed-modules: @@ -86,7 +86,7 @@ library prelude test-suite test import: - common-extensions + extensions main-is: Spec.hs hs-source-dirs: From 9c5695fb21ef6dc328483e1f8592d07de4113b30 Mon Sep 17 00:00:00 2001 From: Jonathan Knowles Date: Sun, 5 Feb 2023 05:00:38 +0000 Subject: [PATCH 18/25] Remove colon qualifiers for internal dependencies in cabal file. --- quickcheck-groups.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/quickcheck-groups.cabal b/quickcheck-groups.cabal index 79e81ed..d6cade2 100644 --- a/quickcheck-groups.cabal +++ b/quickcheck-groups.cabal @@ -47,7 +47,7 @@ library , groups , QuickCheck , quickcheck-classes - , quickcheck-groups:internal + , internal library internal import: @@ -81,7 +81,7 @@ library prelude , groups , QuickCheck , quickcheck-groups - , quickcheck-groups:internal + , internal , quickcheck-instances test-suite test From 64fb3839d32250c1a06ad0ed19723fa00842b394 Mon Sep 17 00:00:00 2001 From: Jonathan Knowles Date: Sun, 5 Feb 2023 05:14:10 +0000 Subject: [PATCH 19/25] Centralise all package dependencies. --- quickcheck-groups.cabal | 67 ++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/quickcheck-groups.cabal b/quickcheck-groups.cabal index d6cade2..b364d8c 100644 --- a/quickcheck-groups.cabal +++ b/quickcheck-groups.cabal @@ -17,6 +17,23 @@ description: extra-source-files: README.md +common dependency-base + build-depends:base >= 4.14.3.0 && < 4.18 +common dependency-hspec + build-depends:hspec >= 2.10.7 && < 2.11 +common dependency-groups + build-depends:groups >= 0.5.3 && < 0.6 +common dependency-pretty-show + build-depends:pretty-show >= 1.10 && < 1.11 +common dependency-QuickCheck + build-depends:QuickCheck >= 2.14.2 && < 2.15 +common dependency-quickcheck-classes + build-depends:quickcheck-classes >= 0.6.5.0 && < 0.7 +common dependency-quickcheck-instances + build-depends:quickcheck-instances >= 0.3.28 && < 0.4 +common dependency-semigroupoids + build-depends:semigroupoids >= 5.3.7 && < 5.4 + common extensions default-extensions: DerivingStrategies @@ -35,7 +52,11 @@ source-repository head library import: - extensions + , dependency-base + , dependency-groups + , dependency-QuickCheck + , dependency-quickcheck-classes + , extensions hs-source-dirs: src/public exposed-modules: @@ -43,15 +64,15 @@ library default-language: Haskell2010 build-depends: - , base >=4.7 && <5 - , groups - , QuickCheck - , quickcheck-classes , internal library internal import: - extensions + , dependency-base + , dependency-pretty-show + , dependency-QuickCheck + , dependency-semigroupoids + , extensions hs-source-dirs: src/internal exposed-modules: @@ -60,15 +81,14 @@ library internal Internal.Semigroup.Tuple default-language: Haskell2010 - build-depends: - , base >=4.7 && <5 - , pretty-show - , QuickCheck - , semigroupoids library prelude import: - extensions + , dependency-base + , dependency-groups + , dependency-QuickCheck + , dependency-quickcheck-instances + , extensions hs-source-dirs: src/prelude exposed-modules: @@ -76,17 +96,17 @@ library prelude default-language: Haskell2010 build-depends: - , base >=4.7 && <5 - , containers - , groups - , QuickCheck , quickcheck-groups - , internal - , quickcheck-instances test-suite test import: - extensions + , dependency-base + , dependency-hspec + , dependency-groups + , dependency-QuickCheck + , dependency-quickcheck-classes + , dependency-quickcheck-instances + , extensions main-is: Spec.hs hs-source-dirs: @@ -100,13 +120,4 @@ test-suite test build-tool-depends: hspec-discover:hspec-discover ==2.* build-depends: - , base >=4.7 && <5 - , bytestring - , containers - , groups - , hspec - , QuickCheck - , quickcheck-classes , quickcheck-groups - , quickcheck-instances - , text From b44438b43926fbc32c35924113df1cce7cb6909e Mon Sep 17 00:00:00 2001 From: Jonathan Knowles Date: Sun, 5 Feb 2023 05:17:03 +0000 Subject: [PATCH 20/25] Remove dependency on `quickcheck-instances` from test suite. --- quickcheck-groups.cabal | 1 - src/test/ClassSpec.hs | 6 ------ 2 files changed, 7 deletions(-) diff --git a/quickcheck-groups.cabal b/quickcheck-groups.cabal index b364d8c..8833611 100644 --- a/quickcheck-groups.cabal +++ b/quickcheck-groups.cabal @@ -105,7 +105,6 @@ test-suite test , dependency-groups , dependency-QuickCheck , dependency-quickcheck-classes - , dependency-quickcheck-instances , extensions main-is: Spec.hs diff --git a/src/test/ClassSpec.hs b/src/test/ClassSpec.hs index 4bfc9e5..c7d2fb8 100644 --- a/src/test/ClassSpec.hs +++ b/src/test/ClassSpec.hs @@ -23,12 +23,6 @@ import Test.QuickCheck.Classes ( Laws (..) ) import Test.QuickCheck.Classes.Group ( abelianLaws, groupLaws ) -import Test.QuickCheck.Instances.ByteString - () -import Test.QuickCheck.Instances.Natural - () -import Test.QuickCheck.Instances.Text - () import Test.QuickCheck.Property ( Result (..), mapTotalResult ) From 6fa9004e2e2fa8d84fb0b7272d1f486d02d01cba Mon Sep 17 00:00:00 2001 From: Jonathan Knowles Date: Sun, 5 Feb 2023 05:20:09 +0000 Subject: [PATCH 21/25] Relocate module `Prelude`. --- quickcheck-groups.cabal | 2 +- .../{Test/QuickCheck/Classes/Group => Internal}/Prelude.hs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/prelude/{Test/QuickCheck/Classes/Group => Internal}/Prelude.hs (89%) diff --git a/quickcheck-groups.cabal b/quickcheck-groups.cabal index 8833611..f501242 100644 --- a/quickcheck-groups.cabal +++ b/quickcheck-groups.cabal @@ -92,7 +92,7 @@ library prelude hs-source-dirs: src/prelude exposed-modules: - Test.QuickCheck.Classes.Group.Prelude + Internal.Prelude default-language: Haskell2010 build-depends: diff --git a/src/prelude/Test/QuickCheck/Classes/Group/Prelude.hs b/src/prelude/Internal/Prelude.hs similarity index 89% rename from src/prelude/Test/QuickCheck/Classes/Group/Prelude.hs rename to src/prelude/Internal/Prelude.hs index c0321a8..2fd9eae 100644 --- a/src/prelude/Test/QuickCheck/Classes/Group/Prelude.hs +++ b/src/prelude/Internal/Prelude.hs @@ -2,7 +2,7 @@ -- Copyright: © 2022–2023 Jonathan Knowles -- License: Apache-2.0 -- -module Test.QuickCheck.Classes.Group.Prelude +module Internal.Prelude ( module Data.Group , module Numeric.Natural , module Test.QuickCheck From 1a5b082c1528e52e400eade2a4dfe26b0b66c6da Mon Sep 17 00:00:00 2001 From: Jonathan Knowles Date: Sun, 5 Feb 2023 06:37:45 +0000 Subject: [PATCH 22/25] Add dependency on `quickcheck-classes` to `prelude`. --- quickcheck-groups.cabal | 1 + 1 file changed, 1 insertion(+) diff --git a/quickcheck-groups.cabal b/quickcheck-groups.cabal index f501242..66dfbfc 100644 --- a/quickcheck-groups.cabal +++ b/quickcheck-groups.cabal @@ -87,6 +87,7 @@ library prelude , dependency-base , dependency-groups , dependency-QuickCheck + , dependency-quickcheck-classes , dependency-quickcheck-instances , extensions hs-source-dirs: From abd463f4d97215c37d4c11950d505b4a69549403 Mon Sep 17 00:00:00 2001 From: Jonathan Knowles Date: Sun, 5 Feb 2023 06:38:20 +0000 Subject: [PATCH 23/25] Add usage guide to `README.md`. --- README.md | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ecb2a68..e10b6a8 100644 --- a/README.md +++ b/README.md @@ -1 +1,76 @@ -QuickCheck support for testing instances of classes defined in the `groups` library. +# `quickcheck-groups` + + + +## Overview + +The `quickcheck-groups` library provides: +- [QuickCheck](https://hackage.haskell.org/package/QuickCheck) support for testing instances of type classes defined in the [`groups`](https://hackage.haskell.org/package/groups) library. +- Compatibility with the [`quickcheck-classes`](https://hackage.haskell.org/package/quickcheck-classes) library. +- Reusable properties for type class laws, in the form of [`Laws`](https://hackage.haskell.org/package/quickcheck-classes/docs/Test-QuickCheck-Classes.html#t:Laws) definitions. + +## Usage + +In general, usage is identical to that of the [`quickcheck-classes`](https://hackage.haskell.org/package/quickcheck-classes) library. If you're already familiar with [`quickcheck-classes`](https://hackage.haskell.org/package/quickcheck-classes), then using this library should be straightforward. + +### Testing laws for a single type class + +To test that the laws of a particular class hold for a particular type, use the [`lawsCheck`](https://hackage.haskell.org/package/quickcheck-classes/docs/Test-QuickCheck-Classes.html#v:lawsCheck) function with the [`Laws`](https://hackage.haskell.org/package/quickcheck-classes/docs/Test-QuickCheck-Classes.html#t:Laws) definition for the class you wish to test. + +> #### :stars: Example +> +> To test that the [`Group`](https://hackage.haskell.org/package/groups/docs/Data-Group.html#t:Group) laws hold for the [`Sum`](https://hackage.haskell.org/package/base/docs/Data-Monoid.html#t:Sum) [`Integer`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Integer) type: +> +> ```hs +> import Data.Monoid (Sum) +> import Data.Proxy (Proxy (Proxy)) +> import Test.QuickCheck.Classes (lawsCheck) +> import Test.QuickCheck.Classes.Group (groupLaws) +> +> lawsCheck (groupLaws (Proxy :: Proxy (Sum Integer))) +> ``` +> +> If all tests pass, you should see output similar to: +> +> ```hs +> Group: groupLaw_invert_mempty +++ OK, passed 1 test. +> Group: groupLaw_invert_invert +++ OK, passed 100 tests. +> Group: groupLaw_invert_mappend_1 +++ OK, passed 100 tests. +> Group: groupLaw_invert_mappend_2 +++ OK, passed 100 tests. +> Group: groupLaw_subtract_mempty +++ OK, passed 100 tests. +> Group: groupLaw_subtract_self +++ OK, passed 100 tests. +> Group: groupLaw_subtract_other +++ OK, passed 100 tests. +> Group: groupLaw_pow_zero +++ OK, passed 100 tests. +> Group: groupLaw_pow_nonNegative +++ OK, passed 100 tests. +> Group: groupLaw_pow_nonPositive +++ OK, passed 100 tests. +> ``` + +### Testing laws for multiple type classes + +To test that the laws of __multiple__ classes hold for a particular type, use the [`lawsCheckOne`](https://hackage.haskell.org/package/quickcheck-classes/docs/Test-QuickCheck-Classes.html#v:lawsCheckOne) function with the [`Laws`](https://hackage.haskell.org/package/quickcheck-classes/docs/Test-QuickCheck-Classes.html#t:Laws) definitions for the classes you wish to test. + +> #### :stars: Example +> +> To test that the [`Sum`](https://hackage.haskell.org/package/base/docs/Data-Monoid.html#t:Sum) [`Integer`](https://hackage.haskell.org/package/base/docs/Prelude.html#t:Integer) type satisfies the laws of [`Abelian`](https://hackage.haskell.org/package/groups/docs/Data-Group.html#t:Abelian) and all superclasses: +> +> ```hs +> import Data.Monoid (Sum) +> import Data.Proxy (Proxy (Proxy)) +> import Test.QuickCheck.Classes +> import Test.QuickCheck.Classes.Group +> +> lawsCheckOne (Proxy :: Proxy (Sum Integer)) +> [ semigroupLaws +> , monoidLaws +> , groupLaws +> , abelianLaws +> ] +> ``` + +## Subclasses and superclasses + +Each of the [`Laws`](https://hackage.haskell.org/package/quickcheck-classes/docs/Test-QuickCheck-Classes.html#t:Laws) definitions provided by this library corresponds to exactly __one__ type class, and includes __just__ the laws for that class. Laws for subclasses and superclasses are __not__ automatically included. Therefore, you'll need to __explicitly__ test the laws of every single class you wish to cover. + +## Coverage checks + +This library includes __coverage checks__ to ensure that important cases are covered, and to reduce the probability of test passes that are false positives. These coverage checks are performed automatically. From d8a49758169a96c11430ff3c19f58db777ff3c08 Mon Sep 17 00:00:00 2001 From: Jonathan Knowles Date: Sun, 5 Feb 2023 04:26:00 +0000 Subject: [PATCH 24/25] Set initial library version to `0.0.0.0`. --- quickcheck-groups.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quickcheck-groups.cabal b/quickcheck-groups.cabal index 66dfbfc..45d5146 100644 --- a/quickcheck-groups.cabal +++ b/quickcheck-groups.cabal @@ -1,6 +1,6 @@ cabal-version: 3.0 name: quickcheck-groups -version: 0.0.0 +version: 0.0.0.0 bug-reports: https://github.com/jonathanknowles/quickcheck-groups/issues license: Apache-2.0 license-file: LICENSE From 9c30c9c47449d7bd6b8db1ab61ba8af96fc13997 Mon Sep 17 00:00:00 2001 From: Jonathan Knowles Date: Sun, 5 Feb 2023 04:58:29 +0000 Subject: [PATCH 25/25] Add `CHANGELOG.md`. --- CHANGELOG.md | 5 +++++ quickcheck-groups.cabal | 1 + 2 files changed, 6 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..4bccaf7 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,5 @@ +# 0.0.0.0 + +- Provides support for testing instances of classes defined in the following + modules: + - `Data.Group` diff --git a/quickcheck-groups.cabal b/quickcheck-groups.cabal index 45d5146..02045ee 100644 --- a/quickcheck-groups.cabal +++ b/quickcheck-groups.cabal @@ -15,6 +15,7 @@ description: groups library. extra-source-files: + CHANGELOG.md README.md common dependency-base