packages feed

leancheck 0.9.0 → 0.9.1

raw patch · 13 files changed

+364/−23 lines, 13 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Test.LeanCheck.Generic: instance Test.LeanCheck.Generic.Listable' f => Test.LeanCheck.Generic.Listable' (GHC.Generics.M1 i c f)
+ Test.LeanCheck.Generic: instance Test.LeanCheck.Generic.Listable' (GHC.Generics.C1 c GHC.Generics.U1)
+ Test.LeanCheck.Generic: instance Test.LeanCheck.Generic.Listable' f => Test.LeanCheck.Generic.Listable' (GHC.Generics.C1 c f)
+ Test.LeanCheck.Generic: instance Test.LeanCheck.Generic.Listable' f => Test.LeanCheck.Generic.Listable' (GHC.Generics.D1 c f)
+ Test.LeanCheck.Generic: instance Test.LeanCheck.Generic.Listable' f => Test.LeanCheck.Generic.Listable' (GHC.Generics.S1 c f)

Files

.travis.yml view
@@ -47,8 +47,8 @@     env:                   GHCVER=head         CABALVER=head     addons: {apt: {packages: [ghc-head,   cabal-install-head], sources: hvr-ghc}}   - ghc: '8.6'-    env:                   GHCVER=8.6.1        CABALVER=2.4-    addons: {apt: {packages: [ghc-8.6.1,  cabal-install-2.4],  sources: hvr-ghc}}+    env:                   GHCVER=8.6.3        CABALVER=2.4+    addons: {apt: {packages: [ghc-8.6.3,  cabal-install-2.4],  sources: hvr-ghc}}   - ghc: '8.4'     env:                   GHCVER=8.4.4        CABALVER=2.2     addons: {apt: {packages: [ghc-8.4.4,  cabal-install-2.2],  sources: hvr-ghc}}
Makefile view
@@ -115,10 +115,14 @@ 	  --ignore "Redundant bracket" \ 	  . -markdown:-	pandoc README.md -o README.html-	pandoc doc/tutorial.md -o doc/tutorial.html-	pandoc doc/data-invariant.md -o doc/data-invariant.html+markdown: \+  README.html \+  doc/tutorial.html \+  doc/data-invariant.html \+  doc/faq.html++%.html: %.md+	pandoc $< -o $@  # NOTE: (very hacky!) the following target allows parallel compilation (-jN) of # eg and tests programs so long as they don't share dependencies _not_ stored
README.md view
@@ -209,7 +209,7 @@ [LeanCheck provider for Tasty]:          https://hackage.haskell.org/package/tasty-leancheck [LeanCheck provider for test-framework]: https://hackage.haskell.org/package/test-framework-leancheck [LeanCheck provider for Hspec]:          https://hackage.haskell.org/package/hspec-leancheck-[leancheck-instances]:                   https://hackage.haskell.org/package/hspec-leancheck+[leancheck-instances]:                   https://hackage.haskell.org/package/leancheck-instances [the intentional exception of a few types]: https://hackage.haskell.org/package/leancheck/docs/Test-LeanCheck-Basic.html [Haskell 2010 Language Report]:          https://www.haskell.org/onlinereport/haskell2010/ [Haskell Platform]:                      https://www.haskell.org/platform/@@ -220,8 +220,8 @@ [build-log]:    https://travis-ci.org/rudymatela/leancheck [hackage-version]: https://img.shields.io/hackage/v/leancheck.svg [leancheck-on-hackage]: https://hackage.haskell.org/package/leancheck-[stackage-lts-badge]:            http://stackage.org/package/leancheck/badge/lts-[stackage-nightly-badge]:        http://stackage.org/package/leancheck/badge/nightly-[leancheck-on-stackage]:         http://stackage.org/package/leancheck-[leancheck-on-stackage-lts]:     http://stackage.org/lts/package/leancheck-[leancheck-on-stackage-nightly]: http://stackage.org/nightly/package/leancheck+[stackage-lts-badge]:            https://stackage.org/package/leancheck/badge/lts+[stackage-nightly-badge]:        https://stackage.org/package/leancheck/badge/nightly+[leancheck-on-stackage]:         https://stackage.org/package/leancheck+[leancheck-on-stackage-lts]:     https://stackage.org/lts/package/leancheck+[leancheck-on-stackage-nightly]: https://stackage.org/nightly/package/leancheck
changelog.md view
@@ -1,6 +1,14 @@ Changelog for LeanCheck ======================= +v0.9.1+------++* fix bug in `genericTiers` where using it bound to a recursive datatype could+  cause an infinite loop;+* minor improvements in documentation and tests.++ v0.9.0 ------ 
+ doc/faq.md view
@@ -0,0 +1,177 @@+LeanCheck FAQ+=============+++What is LeanCheck?+------------------++[LeanCheck] is a (property-based) testing tool for Haskell.  It provides a+`check` function that takes a property, tests it by automatically generating+test values, then reports the results.++More details on the [LeanCheck tutorial].+++What is property-based testing (PBT)?+-------------------------------------++In property-based testing, tests are defined as functions returning a boolean+value which should be true for all choices of argument values.  We call these+tests properties.  A property-based testing tool can then generate arguments+automatically to search for a failing test case.++Property-based testing is also known as:+* property testing;+* parameterized unit testing.++More details on the [LeanCheck tutorial].+++What are the differences between QuickCheck and LeanCheck?+----------------------------------------------------------++[QuickCheck] and [LeanCheck] are both property-based testing tools for Haskell.+Some of the differences follow:++__Test case generation.__  In QuickCheck, test cases are generated randomly for+types that are instances of the [`Arbitrary`] typeclass.  In LeanCheck, test+cases are generated enumeratively for types that are instances of the+[`Listable`] typeclass.  The next section in this FAQ expands on this+difference.++__Purity.__  Differently from QuickCheck, LeanCheck generators are purely+functional.  To get a list of all booleans you simply do:++	> list :: [Bool]+	[False, True]++To get a (infinite) list of all integers, you simply do:++	> list :: [Int]+	[0, 1, 2, 3, 4, 5, 6, 7, ...]++__Memory consumption.__  LeanCheck is more memory intensive when compared to+QuickCheck.  With LeanCheck you may run out of memory when you're running tens+of millions of tests depending on your datatype.++++What are the differences between enumerative and random testing?+----------------------------------------------------------------++__Counterexample size.__ When using enumerative testing, reported+counterexamples are guaranteed to be the smallest or simplest as possible.+Most of the times, the smaller the counterexample, the easier it is to find the+cause of the fault.  When doing random testing, you may sometimes get a bigger+counterexample and you'll have to resort to [shrinking] which is enabled by+default on [`Arbitrary`] instances that define a [`shrink`] function.+Depending on your datatype, shrinking can be effective.++	> import Test.LeanCheck+	> check $ \xs ys -> xs `union` ys == ys `union` (xs :: [Int])+	*** Failed! Falsifiable (after 4 tests):+	[] [0,0]++	> import Test.QuickCheck+	> quickCheck . noShrinking $ \xs ys -> xs `union` ys == ys `union` (xs :: [Int])+	*** Failed! Falsifiable (after 3 tests):+	[2,-1]+	[0,2]++	> quickCheck $ \xs ys -> xs `union` ys == ys `union` (xs :: [Int])+	*** Failed! Falsifiable (after 3 tests):+	[]+	[2,2]++__Existential properties.__ Enumerative testing tools allow for the definition+of existential properties:++	prop_lessThanExists :: Natural -> Natural -> Bool+	prop_lessThanExists x y = x <= y ==> exists 1000 $ \z -> x + z == y++Random testing does not allow it without the use of sometimes-not-so-obvious+process of [Skolemization].++__Coverage.__ Random testing always hits different test cases, so in the long+term you may get more test coverage.  With enumerative testing you only get+more coverage when you configure more tests.++__Ease of use.__ In my humble opinion, writing generators for enumerative+testing tools is easier than on random testing tools.  Nevertheless, the+availability of automatic derivation makes this a minor issue.+++What are the differences between LeanCheck and Feat?+----------------------------------------------------++[LeanCheck] and [Feat] are both size-bounded enumerative testing tools.  There+are a few differences.  Some are detailed in the following paragraphs.++__Enumeration.__  Choices when defining enumeration were different on LeanCheck+and Feat.  For some types, like `Bool` and `[Bool]` the enumeration is+isomorphic (has the same order).  For other types, like `Int` and `[Int]`, the+enumeration is different.  In LeanCheck, [`tiers`] of values are as thin as+possible, this makes the enumeration less likely to "blow-up" when tupling.+Take for example, the enumeration for trios of integers,+`(Int,Int,Int)`:++	> import Test.LeanCheck+	> take 10 $ map length (tiers :: [[(Int,Int,Int)]])+	[1,3,6,10,15,21,28,36,45,55]++	> import Test.Feat+	> take 10 $ map fst ( values :: [(Integer, [(Int,Int,Int)])] )+	[8,24,72,200,528,1344,3328,8064,19200,45056]++As you nest, the difference increases:++	> import Test.LeanCheck+	> take 10 $ map length (tiers :: [[ [(Int,Int,Int)] ]])+	[1,1,4,13,41,129,406,1278,4023,12664]++	> import Test.Feat+	> take 10 $ map fst ( values :: [(Integer, [[(Int,Int,Int)]]  )] )+	[0,1,8,88,968,10632,116752,1282048,14078080,154590400]++__Interface.__  The API of these tools differ in several ways.  Two examples:++* LeanCheck supports ([`Testable`]) properties with multiple arguments+  out-of-the-box whereas Feat requires, at least by default, properties to be+  uncurried.+* LeanCheck is configured by the number of tests whereas Feat is configured by+  the size of tests.++__Memory consumption.__  LeanCheck is more memory intensive than Feat.++__Random testing.__  Feat is able to do random testing.  LeanCheck is not (at+least not with good performance).++__Implementation.__  The [implementation and internals of LeanCheck] are+simpler than those of Feat.++__Tool support.__  Tool support for LeanCheck and Feat is different.+[LeanCheck has bindings] for [Tasty], [test-framework] and [Hspec].  As of Jan+2019, [Feat only has bindings for test-framework], nevertheless adding support+for Tasty and Hspec would be simple.++LeanCheck facilitates the use of [Extrapolate] to generalize counterexamples.+++[LeanCheck]:    https://hackage.haskell.org/package/leancheck+[QuickCheck]:   https://hackage.haskell.org/package/QuickCheck+[Feat]:         https://hackage.haskell.org/package/testing-feat+[Extrapolate]:  https://hackage.haskell.org/package/extrapolate+[LeanCheck tutorial]: doc/tutorial.md+[`Listable`]:  https://hackage.haskell.org/package/leancheck/docs/Test-LeanCheck.html#t:Listable+[`Testable`]:  https://hackage.haskell.org/package/leancheck/docs/Test-LeanCheck.html#t:Testable+[`tiers`]:     https://hackage.haskell.org/package/leancheck/docs/Test-LeanCheck.html#v:tiers+[`Arbitrary`]: https://hackage.haskell.org/package/QuickCheck/docs/Test-QuickCheck.html#t:Arbitrary+[shrinking]:   https://hackage.haskell.org/package/QuickCheck/docs/Test-QuickCheck.html#v:shrink+[`shrink`]:    https://hackage.haskell.org/package/QuickCheck/docs/Test-QuickCheck.html#v:shrink+[Skolemization]: https://en.wikipedia.org/wiki/Skolem_normal_form+[Tasty]:          https://github.com/feuerbach/tasty#readme+[test-framework]: https://haskell.github.io/test-framework/+[Hspec]:          https://hspec.github.io/+[LeanCheck has bindings]: https://github.com/rudymatela/leancheck#providers-for-tasty-test-framework-and-hspec+[Feat only has bindings for test-framework]: https://hackage.haskell.org/package/test-framework-testing-feat+[implementation and internals of LeanCheck]: https://hackage.haskell.org/package/leancheck/docs/src/Test.LeanCheck.Core.html
doc/tutorial.md view
@@ -203,9 +203,16 @@  LeanCheck also provides the function [`deriveListable`] to automatically derive [`Listable`] instances for types that do not follow a data invariant (precondition).+The above [`Listable`] instance could be replaced by simply: +	{-# LANGUAGE TemplateHaskell #-}+	...+	... +	deriveListable ''Stack ++ Advantages of property-based testing ------------------------------------ @@ -215,7 +222,7 @@   after making a small change to a program, we might [`checkFor`] `50` tests;   before making a major release, we may [`checkFor`] `1000` tests.   A continuous integration system can be configured to run more test than what-  is usual on developers machines.+  is usual on the development environment.    * (+) documentation:   properties serve as a clear documentation of behaviour;@@ -239,6 +246,7 @@ ----------------------------------------------  * [QuickCheck]      : randomized+* [Hedgehog]        : randomized * [SmallCheck]      : enumerative, depth-bounded * [Lazy SmallCheck] : enumerative, depth-bounded, lazy, demand-driven * [Feat]            : enumerative, size-bounded@@ -260,6 +268,7 @@ [`deriveListable`]: https://hackage.haskell.org/package/leancheck/docs/Test-LeanCheck.html#v:deriveListable  [QuickCheck]:      https://hackage.haskell.org/package/QuickCheck+[Hedgehog]:        https://hackage.haskell.org/package/hedgehog [SmallCheck]:      https://hackage.haskell.org/package/smallcheck [Lazy SmallCheck]: https://hackage.haskell.org/package/lazysmallcheck [Feat]:            https://hackage.haskell.org/package/testing-feat
leancheck.cabal view
@@ -11,7 +11,7 @@ -- this cabal file too complicated.  -- Rudy  name:                leancheck-version:             0.9.0+version:             0.9.1 synopsis:            Enumerative property-based testing description:   LeanCheck is a simple enumerative property-based testing library.@@ -40,6 +40,7 @@                , TODO.md                , changelog.md                , doc/data-invariant.md+               , doc/faq.md                , doc/tutorial.md                , doc/leancheck.svg extra-source-files: .gitignore@@ -74,7 +75,7 @@ source-repository this   type:            git   location:        https://github.com/rudymatela/leancheck-  tag:             v0.9.0+  tag:             v0.9.1  library   exposed-modules: Test.LeanCheck
mk/depend.mk view
@@ -382,8 +382,11 @@   test/Test.hs \   test/derive.hs \   src/Test/LeanCheck/Utils/Types.hs \+  src/Test/LeanCheck/Utils/TypeBinding.hs \+  src/Test/LeanCheck/Utils.hs \   src/Test/LeanCheck/Utils/Operators.hs \   src/Test/LeanCheck/Tiers.hs \+  src/Test/LeanCheck/Stats.hs \   src/Test/LeanCheck.hs \   src/Test/LeanCheck/IO.hs \   src/Test/LeanCheck/Derive.hs \@@ -451,8 +454,11 @@   test/Test.hs \   test/generic.hs \   src/Test/LeanCheck/Utils/Types.hs \+  src/Test/LeanCheck/Utils/TypeBinding.hs \+  src/Test/LeanCheck/Utils.hs \   src/Test/LeanCheck/Utils/Operators.hs \   src/Test/LeanCheck/Tiers.hs \+  src/Test/LeanCheck/Stats.hs \   src/Test/LeanCheck.hs \   src/Test/LeanCheck/IO.hs \   src/Test/LeanCheck/Generic.hs \
src/Test/LeanCheck/Function.hs view
@@ -43,8 +43,8 @@ -- -- The 'Listable' and 'Show' function instance are defined in, respectively: ----- * "Test.LeanCheck.Function.Listable";--- * "Test.LeanCheck.Function.Show".+-- * "Test.LeanCheck.Function.Listable"+-- * "Test.LeanCheck.Function.Show" -- -- The 'Show' instance will work for all functions whose return types are -- instances of ShowFunction from "Test.LeanCheck.Function.ShowFunction".
src/Test/LeanCheck/Generic.hs view
@@ -1,4 +1,7 @@-{-# LANGUAGE FlexibleContexts, TypeOperators #-}+{-# LANGUAGE CPP, FlexibleContexts, FlexibleInstances, TypeOperators #-}+#if __GLASGOW_HASKELL__ < 710+{-# LANGUAGE OverlappingInstances #-}+#endif -- | -- Module      : Test.LeanCheck.Generic -- Copyright   : (c) 2018 Rudy Matela@@ -61,5 +64,25 @@ instance (Listable' a, Listable' b) => Listable' (a :*: b) where   tiers' = productWith (:*:) tiers' tiers' -instance Listable' f => Listable' (M1 i c f) where+instance Listable' f => Listable' (S1 c f) where   tiers' = mapT M1 tiers'++instance Listable' f => Listable' (D1 c f) where+  tiers' = mapT M1 tiers'++#if __GLASGOW_HASKELL__ >= 710+-- don't delay when there is a constructor with 0 arguments+instance {-# OVERLAPPING #-} Listable' (C1 c U1) where+  tiers' = mapT M1 tiers'++-- delay when there is a constructor with 1 or more arguments+instance {-# OVERLAPPABLE #-} Listable' f => Listable' (C1 c f) where+  tiers' = delay $ mapT M1 tiers'+#else++instance Listable' (C1 c U1)+  where tiers' = mapT M1 tiers'++instance Listable' f => Listable' (C1 c f)+  where tiers' = delay $ mapT M1 tiers'+#endif
stack.yaml view
@@ -3,7 +3,7 @@  # resolver: nightly-2015-09-21 # resolver: ghc-7.10.2-resolver: lts-12.9+resolver: lts-13.6  packages: - .
test/derive.hs view
@@ -6,7 +6,7 @@ import Test.LeanCheck.Derive import System.Exit (exitFailure) import Data.List (elemIndices,sort)-import Test.LeanCheck.Utils.Operators+import Test.LeanCheck.Utils  data D0       = D0                    deriving Show data D1 a     = D1 a                  deriving Show@@ -24,6 +24,17 @@ deriveListable ''C2 deriveListable ''I +-- recursive datatypes+data Peano = Zero | Succ Peano deriving Show+data List a = a :- List a | Nil deriving Show+data Bush a = Bush a :-: Bush a | Leaf a deriving (Show, Eq)+data Tree a = Node (Tree a) a (Tree a) | Null deriving (Show, Eq)++deriveListable ''Peano+deriveListable ''List+deriveListable ''Bush+deriveListable ''Tree+ -- Nested datatype cascade data Nested = Nested N0 (N1 Int) (N2 Int Int) data N0     = R0 Int@@ -86,6 +97,32 @@   , map unD2 list == (list :: [(Bool,Bool)])   , map unD3 list == (list :: [(Bool,Bool,Bool)]) +  , map peanoToNat list =| n |= list+  , map listToList list =| n |= (list :: [[Bool]])+  , map listToList list =| n |= (list :: [[Int]])++  , mapT peanoToNat tiers =| 6 |= tiers+  , mapT listToList tiers =| 6 |= (tiers :: [[ [Bool] ]])+  , mapT listToList tiers =| 6 |= (tiers :: [[ [Int] ]])++  , take 6 (list :: [Bush Bool])+    == [ Leaf False+       , Leaf True+       , Leaf False :-: Leaf False+       , Leaf False :-: Leaf True+       , Leaf True :-: Leaf False+       , Leaf True :-: Leaf True+       ]++  , take 6 (list :: [Tree Bool])+    == [ Null+       , Node Null False Null+       , Node Null True Null+       , Node Null False (Node Null False Null)+       , Node Null False (Node Null True Null)+       , Node Null True (Node Null False Null)+       ]+   , (tiers :: [[ Bool       ]]) =| 6 |= $(deriveTiers ''Bool)   , (tiers :: [[ [Int]      ]]) =| 6 |= $(deriveTiers ''[])   , (tiers :: [[ [Bool]     ]]) =| 6 |= $(deriveTiers ''[])@@ -104,3 +141,11 @@   unD1 (D1 x)     = (x)   unD2 (D2 x y)   = (x,y)   unD3 (D3 x y z) = (x,y,z)++peanoToNat :: Peano -> Nat+peanoToNat Zero = 0+peanoToNat (Succ n) = 1 + peanoToNat n++listToList :: List a -> [a]+listToList Nil = []+listToList (x :- xs) = x : listToList xs
test/generic.hs view
@@ -1,12 +1,13 @@ -- Copyright (c) 2015-2018 Rudy Matela. -- Distributed under the 3-Clause BSD licence (see the file LICENSE).-{-# LANGUAGE DeriveGeneric, StandaloneDeriving #-}+{-# LANGUAGE DeriveGeneric, StandaloneDeriving, TemplateHaskell #-} import Test import Test.LeanCheck import Test.LeanCheck.Generic+import Test.LeanCheck.Derive (deriveTiers) import System.Exit (exitFailure) import Data.List (elemIndices,sort)-import Test.LeanCheck.Utils.Operators+import Test.LeanCheck.Utils import GHC.Generics (Generic)  data D0       = D0                    deriving (Eq, Show, Generic)@@ -35,6 +36,18 @@ instance (Listable a, Listable b)       => Listable (I a b)              where tiers = genericTiers +-- recursive datatypes+data Peano = Zero | Succ Peano deriving (Show, Generic)+data List a = a :- List a | Nil deriving (Show, Generic)+data Bush a = Bush a :-: Bush a | Leaf a deriving (Show, Eq, Generic)+data Tree a = Node (Tree a) a (Tree a) | Null deriving (Show, Eq, Generic)++instance Listable Peano where tiers = genericTiers+instance Listable a => Listable (List a) where tiers = genericTiers+instance Listable a => Listable (Bush a) where tiers = genericTiers+instance Listable a => Listable (Tree a) where tiers = genericTiers++ main :: IO () main =   case elemIndices False (tests 100) of@@ -57,9 +70,64 @@   , map unD1 list == (list :: [Bool])   , map unD2 list == (list :: [(Bool,Bool)])   , map unD3 list == (list :: [(Bool,Bool,Bool)])++  , map peanoToNat list =| n |= list+  , map listToList list =| n |= (list :: [[Bool]])+  , map listToList list =| n |= (list :: [[Int]])++  , mapT peanoToNat tiers =| 6 |= tiers+  , mapT listToList tiers =| 6 |= (tiers :: [[ [Bool] ]])+  , mapT listToList tiers =| 6 |= (tiers :: [[ [Int] ]])++  , take 6 (list :: [Bush Bool])+    == [ Leaf False+       , Leaf True+       , Leaf False :-: Leaf False+       , Leaf False :-: Leaf True+       , Leaf True :-: Leaf False+       , Leaf True :-: Leaf True+       ]++  , take 6 (list :: [Tree Bool])+    == [ Null+       , Node Null False Null+       , Node Null True Null+       , Node Null False (Node Null False Null)+       , Node Null False (Node Null True Null)+       , Node Null True (Node Null False Null)+       ]++  , (tiers :: [[ Bool       ]]) =| 6 |= genericTiers+  , (tiers :: [[ [Int]      ]]) =| 6 |= genericTiers+  , (tiers :: [[ [Bool]     ]]) =| 6 |= genericTiers+  , (tiers :: [[ Maybe Int  ]]) =| 6 |= genericTiers+  , (tiers :: [[ Maybe Bool ]]) =| 6 |= genericTiers+  , ([]:tiers :: [[Either Bool Int]]) =$ map sort . take 6 $= genericTiers++  , (list :: [ Bool       ]) =| n |= genericList+  , (list :: [ [Int]      ]) =| n |= genericList+  , (list :: [ [Bool]     ]) =| n |= genericList+  , (list :: [ Maybe Int  ]) =| n |= genericList+  , (list :: [ Maybe Bool ]) =| n |= genericList++  -- test consistency with deriveTiers+  , (genericTiers :: [[ Bool ]])             =| 6 |=  $(deriveTiers ''Bool)+  , (genericTiers :: [[ [Int]      ]])       =| 6 |=  $(deriveTiers ''[])+  , (genericTiers :: [[ [Bool]     ]])       =| 6 |=  $(deriveTiers ''[])+  , (genericTiers :: [[ Maybe Int  ]])       =| 6 |=  $(deriveTiers ''Maybe)+  , (genericTiers :: [[ Maybe Bool ]])       =| 6 |=  $(deriveTiers ''Maybe)+  , (genericTiers :: [[ Either Bool Int ]])  =| 6 |=  $(deriveTiers ''Either)   ]   where   unD0 (D0)       = ()   unD1 (D1 x)     = (x)   unD2 (D2 x y)   = (x,y)   unD3 (D3 x y z) = (x,y,z)++peanoToNat :: Peano -> Nat+peanoToNat Zero = 0+peanoToNat (Succ n) = 1 + peanoToNat n++listToList :: List a -> [a]+listToList Nil = []+listToList (x :- xs) = x : listToList xs