Skip to content

Commit 7c91aab

Browse files
Preparation for release. (#15)
2 parents 02afd6c + 9c30c9c commit 7c91aab

File tree

10 files changed

+411
-129
lines changed

10 files changed

+411
-129
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# 0.0.0.0
2+
3+
- Provides support for testing instances of classes defined in the following
4+
modules:
5+
- `Data.Group`

README.md

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,76 @@
1-
QuickCheck support for testing instances of classes defined in the `groups` library.
1+
# `quickcheck-groups`
2+
3+
<a href="http://jonathanknowles.net/quickcheck-groups/"><img src="https://img.shields.io/badge/API-Documentation-green" /></a>
4+
5+
## Overview
6+
7+
The `quickcheck-groups` library provides:
8+
- [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.
9+
- Compatibility with the [`quickcheck-classes`](https://hackage.haskell.org/package/quickcheck-classes) library.
10+
- 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.
11+
12+
## Usage
13+
14+
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.
15+
16+
### Testing laws for a single type class
17+
18+
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.
19+
20+
> #### :stars: Example
21+
>
22+
> 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:
23+
>
24+
> ```hs
25+
> import Data.Monoid (Sum)
26+
> import Data.Proxy (Proxy (Proxy))
27+
> import Test.QuickCheck.Classes (lawsCheck)
28+
> import Test.QuickCheck.Classes.Group (groupLaws)
29+
>
30+
> lawsCheck (groupLaws (Proxy :: Proxy (Sum Integer)))
31+
> ```
32+
>
33+
> If all tests pass, you should see output similar to:
34+
>
35+
> ```hs
36+
> Group: groupLaw_invert_mempty +++ OK, passed 1 test.
37+
> Group: groupLaw_invert_invert +++ OK, passed 100 tests.
38+
> Group: groupLaw_invert_mappend_1 +++ OK, passed 100 tests.
39+
> Group: groupLaw_invert_mappend_2 +++ OK, passed 100 tests.
40+
> Group: groupLaw_subtract_mempty +++ OK, passed 100 tests.
41+
> Group: groupLaw_subtract_self +++ OK, passed 100 tests.
42+
> Group: groupLaw_subtract_other +++ OK, passed 100 tests.
43+
> Group: groupLaw_pow_zero +++ OK, passed 100 tests.
44+
> Group: groupLaw_pow_nonNegative +++ OK, passed 100 tests.
45+
> Group: groupLaw_pow_nonPositive +++ OK, passed 100 tests.
46+
> ```
47+
48+
### Testing laws for multiple type classes
49+
50+
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.
51+
52+
> #### :stars: Example
53+
>
54+
> 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:
55+
>
56+
> ```hs
57+
> import Data.Monoid (Sum)
58+
> import Data.Proxy (Proxy (Proxy))
59+
> import Test.QuickCheck.Classes
60+
> import Test.QuickCheck.Classes.Group
61+
>
62+
> lawsCheckOne (Proxy :: Proxy (Sum Integer))
63+
> [ semigroupLaws
64+
> , monoidLaws
65+
> , groupLaws
66+
> , abelianLaws
67+
> ]
68+
> ```
69+
70+
## Subclasses and superclasses
71+
72+
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.
73+
74+
## Coverage checks
75+
76+
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.

quickcheck-groups.cabal

Lines changed: 52 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,41 @@
11
cabal-version: 3.0
22
name: quickcheck-groups
3-
version: 0.0.0
3+
version: 0.0.0.0
44
bug-reports: https://github.com/jonathanknowles/quickcheck-groups/issues
55
license: Apache-2.0
66
license-file: LICENSE
77
author: Jonathan Knowles
88
maintainer: mail@jonathanknowles.net
99
copyright: 2022–2023 Jonathan Knowles
1010
category: Testing
11-
synopsis: QuickCheck support for testing instances of classes defined in
12-
the groups library.
11+
synopsis: Testing group class instances with QuickCheck
1312
description:
1413

15-
This library provides:
16-
17-
* QuickCheck support for testing instances of classes defined in the
18-
'groups' library.
19-
* Reusable properties in the form of 'Laws' definitions.
14+
QuickCheck support for testing instances of type classes defined in the
15+
groups library.
2016

2117
extra-source-files:
18+
CHANGELOG.md
2219
README.md
2320

24-
common common-extensions
21+
common dependency-base
22+
build-depends:base >= 4.14.3.0 && < 4.18
23+
common dependency-hspec
24+
build-depends:hspec >= 2.10.7 && < 2.11
25+
common dependency-groups
26+
build-depends:groups >= 0.5.3 && < 0.6
27+
common dependency-pretty-show
28+
build-depends:pretty-show >= 1.10 && < 1.11
29+
common dependency-QuickCheck
30+
build-depends:QuickCheck >= 2.14.2 && < 2.15
31+
common dependency-quickcheck-classes
32+
build-depends:quickcheck-classes >= 0.6.5.0 && < 0.7
33+
common dependency-quickcheck-instances
34+
build-depends:quickcheck-instances >= 0.3.28 && < 0.4
35+
common dependency-semigroupoids
36+
build-depends:semigroupoids >= 5.3.7 && < 5.4
37+
38+
common extensions
2539
default-extensions:
2640
DerivingStrategies
2741
FlexibleContexts
@@ -39,78 +53,72 @@ source-repository head
3953

4054
library
4155
import:
42-
common-extensions
56+
, dependency-base
57+
, dependency-groups
58+
, dependency-QuickCheck
59+
, dependency-quickcheck-classes
60+
, extensions
4361
hs-source-dirs:
4462
src/public
4563
exposed-modules:
4664
Test.QuickCheck.Classes.Group
4765
default-language:
4866
Haskell2010
4967
build-depends:
50-
, base >=4.7 && <5
51-
, groups
52-
, QuickCheck
53-
, quickcheck-classes
54-
, quickcheck-groups:internal
68+
, internal
5569

5670
library internal
5771
import:
58-
common-extensions
72+
, dependency-base
73+
, dependency-pretty-show
74+
, dependency-QuickCheck
75+
, dependency-semigroupoids
76+
, extensions
5977
hs-source-dirs:
6078
src/internal
6179
exposed-modules:
62-
Data.Semigroup.Eq
63-
Test.QuickCheck.Classes.Group.Internal
64-
Test.QuickCheck.Classes.Group.Tuple
80+
Internal
81+
Internal.Semigroup.Eq
82+
Internal.Semigroup.Tuple
6583
default-language:
6684
Haskell2010
67-
build-depends:
68-
, base >=4.7 && <5
69-
, pretty-show
70-
, QuickCheck
71-
, semigroupoids
7285

7386
library prelude
7487
import:
75-
common-extensions
88+
, dependency-base
89+
, dependency-groups
90+
, dependency-QuickCheck
91+
, dependency-quickcheck-classes
92+
, dependency-quickcheck-instances
93+
, extensions
7694
hs-source-dirs:
7795
src/prelude
7896
exposed-modules:
79-
Test.QuickCheck.Classes.Group.Prelude
97+
Internal.Prelude
8098
default-language:
8199
Haskell2010
82100
build-depends:
83-
, base >=4.7 && <5
84-
, containers
85-
, groups
86-
, QuickCheck
87101
, quickcheck-groups
88-
, quickcheck-groups:internal
89-
, quickcheck-instances
90102

91103
test-suite test
92104
import:
93-
common-extensions
105+
, dependency-base
106+
, dependency-hspec
107+
, dependency-groups
108+
, dependency-QuickCheck
109+
, dependency-quickcheck-classes
110+
, extensions
94111
main-is:
95112
Spec.hs
96113
hs-source-dirs:
97114
src/test
98115
other-modules:
99-
Test.QuickCheck.Classes.Hspec
100-
Test.QuickCheck.Classes.GroupSpec
116+
ClassSpec
117+
Test.Hspec.Laws
101118
type: exitcode-stdio-1.0
102119
default-language:
103120
Haskell2010
104121
build-tool-depends:
105122
hspec-discover:hspec-discover ==2.*
106123
build-depends:
107-
, base >=4.7 && <5
108-
, bytestring
109-
, containers
110-
, groups
111-
, hspec
112-
, QuickCheck
113-
, quickcheck-classes
114124
, quickcheck-groups
115-
, quickcheck-instances
116-
, text

src/internal/Test/QuickCheck/Classes/Group/Internal.hs renamed to src/internal/Internal.hs

Lines changed: 47 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,45 +2,55 @@
22
-- Copyright: © 2022–2023 Jonathan Knowles
33
-- License: Apache-2.0
44
--
5-
module Test.QuickCheck.Classes.Group.Internal
6-
( makeLaw0
5+
module Internal
6+
( cover
7+
, makeLaw0
78
, makeLaw1
89
, makeLaw2
910
, makeLaw3
1011
, makeProperty
12+
, report
13+
, (==>)
1114
)
1215
where
1316

1417
import Data.Function
1518
( (&) )
1619
import Data.Proxy
1720
( Proxy (..) )
18-
import Data.Semigroup.Eq
19-
( allUnique, canVerifyAllNonNull )
21+
import Internal.Semigroup.Eq
22+
( allNonNull, allUnique, allUniqueNonNull )
23+
import Internal.Semigroup.Tuple
24+
( Tuple1, Tuple2, Tuple3, evalTuple1, evalTuple2, evalTuple3 )
2025
import Test.QuickCheck
2126
( Arbitrary (..)
2227
, Property
2328
, Testable
2429
, checkCoverage
2530
, counterexample
26-
, cover
2731
, property
2832
)
29-
import Test.QuickCheck.Classes.Group.Tuple
30-
( Tuple1, Tuple2, Tuple3, evalTuple1, evalTuple2, evalTuple3 )
33+
34+
import qualified Test.QuickCheck as QC
35+
36+
infixr 0 ==>
37+
(==>) :: Bool -> Bool -> Bool
38+
a ==> b = not a || b
39+
40+
cover :: Testable t => String -> Bool -> t -> Property
41+
cover name = flip (QC.cover 0.1) (replaceSpecialChars <$> name)
3142

3243
makeLaw :: Testable t => String -> t -> (String, Property)
3344
makeLaw title t = (title, checkCoverage $ property t)
3445

3546
makeLaw0
36-
:: forall a. (Eq a, Monoid a)
37-
=> String
47+
:: String
3848
-> (Proxy a -> Property)
3949
-> (String, Property)
4050
makeLaw0 s = makeLaw s . makeProperty0
4151

4252
makeLaw1
43-
:: (Arbitrary a, Show a, Eq a, Monoid a, Testable t)
53+
:: (Arbitrary a, Show a, Eq a, Semigroup a, Testable t)
4454
=> String
4555
-> (a -> t)
4656
-> (String, Property)
@@ -70,9 +80,6 @@ makeProperty propertyDescription t =
7080
& fmap replaceSpecialChars
7181
]
7282
where
73-
replaceSpecialChars = \case
74-
'λ' -> '\\'
75-
other -> other
7683

7784
makeProperty0
7885
:: forall a t. Testable t
@@ -81,42 +88,49 @@ makeProperty0
8188
makeProperty0 p = property $ p $ Proxy @a
8289

8390
makeProperty1
84-
:: (Eq a, Monoid a, Testable t)
91+
:: (Eq a, Semigroup a, Testable t)
8592
=> (a -> t)
8693
-> (Tuple1 a -> Property)
8794
makeProperty1 p (evalTuple1 -> a)
88-
= cover 1 (a == mempty) "a == mempty"
89-
$ cover 1 (a /= mempty) "a /= mempty"
90-
$ property $ p a
95+
= property $ p a
9196

9297
makeProperty2
9398
:: (Eq a, Semigroup a, Testable t)
9499
=> (a -> a -> t)
95100
-> (Tuple2 a -> Property)
96101
makeProperty2 p (evalTuple2 -> (a, b))
97-
= cover 1
98-
(allUnique [a, b])
102+
= cover
99103
"allUnique [a, b]"
100-
$ cover 1
101-
(canVerifyAllNonNull [a, b])
102-
"canVerifyAllNonNull [a, b]"
103-
$ cover 1
104-
(allUnique [a, b] && canVerifyAllNonNull [a, b])
105-
"allUnique [a, b] && canVerifyAllNonNull [a, b]"
104+
(allUnique [a, b])
105+
$ cover
106+
"allNonNull [a, b]"
107+
(allNonNull [a, b])
108+
$ cover
109+
"allUniqueNonNull [a, b]"
110+
(allUniqueNonNull [a, b])
106111
$ property $ p a b
107112

108113
makeProperty3
109114
:: (Eq a, Semigroup a, Testable t)
110115
=> (a -> a -> a -> t)
111116
-> (Tuple3 a -> Property)
112117
makeProperty3 p (evalTuple3 -> (a, b, c))
113-
= cover 1
114-
(allUnique [a, b, c])
118+
= cover
115119
"allUnique [a, b, c]"
116-
$ cover 1
117-
(canVerifyAllNonNull [a, b, c])
118-
"canVerifyAllNonNull [a, b, c]"
119-
$ cover 1
120-
(allUnique [a, b, c] && canVerifyAllNonNull [a, b, c])
121-
"allUnique [a, b, c] && canVerifyAllNonNull [a, b, c]"
120+
(allUnique [a, b, c])
121+
$ cover
122+
"allNonNull [a, b, c]"
123+
(allNonNull [a, b, c])
124+
$ cover
125+
"allUniqueNonNull [a, b, c]"
126+
(allUniqueNonNull [a, b, c])
122127
$ property $ p a b c
128+
129+
report :: (Show a, Testable prop) => String -> a -> prop -> Property
130+
report name a = counterexample $
131+
(replaceSpecialChars <$> name) <> ":\n" <> show a <> "\n"
132+
133+
replaceSpecialChars :: Char -> Char
134+
replaceSpecialChars = \case
135+
'λ' -> '\\'
136+
other -> other

0 commit comments

Comments
 (0)