genvalidity 0.2.0.4 → 0.3.0.0
raw patch · 3 files changed
+217/−111 lines, 3 filesdep ~QuickCheckdep ~validityPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: QuickCheck, validity
API changes (from Hackage documentation)
- Data.GenRelativeValidity: class (GenValidity a, RelativeValidity a b) => GenRelativeValidity a b where genUncheckedFor _ = genUnchecked genValidFor b = genValid `suchThat` (`isValidFor` b) genInvalidFor b = genUncheckedFor b `suchThat` (not . (`isValidFor` b))
- Data.GenValidity: class Validity a => GenValidity a where genValid = genUnchecked `suchThat` isValid genInvalid = genUnchecked `suchThat` (not . isValid)
- Data.GenValidity: instance (Data.GenValidity.GenValidity a, Data.GenValidity.GenValidity b) => Data.GenValidity.GenValidity (a, b)
- Data.GenValidity: instance (Data.GenValidity.GenValidity a, Data.GenValidity.GenValidity b, Data.GenValidity.GenValidity c) => Data.GenValidity.GenValidity (a, b, c)
- Data.GenValidity: instance Data.GenValidity.GenValidity a => Data.GenValidity.GenValidity (GHC.Base.Maybe a)
- Data.GenValidity: instance Data.GenValidity.GenValidity a => Data.GenValidity.GenValidity [a]
+ Data.GenRelativeValidity: class (GenUnchecked a, RelativeValidity a b, GenRelativeUnchecked a b) => GenRelativeInvalid a b where genInvalidFor b = genUncheckedFor b `suchThat` (not . (`isValidFor` b))
+ Data.GenRelativeValidity: class (GenUnchecked a, RelativeValidity a b) => GenRelativeUnchecked a b where genUncheckedFor _ = genUnchecked
+ Data.GenRelativeValidity: class (GenValid a, RelativeValidity a b) => GenRelativeValid a b where genValidFor b = genValid `suchThat` (`isValidFor` b)
+ Data.GenValidity: class (Validity a, GenUnchecked a) => GenInvalid a where genInvalid = genUnchecked `suchThat` (not . isValid)
+ Data.GenValidity: class GenUnchecked a
+ Data.GenValidity: class (Validity a, GenUnchecked a) => GenValid a where genValid = genUnchecked `suchThat` isValid
+ Data.GenValidity: instance (Data.GenValidity.GenInvalid a, Data.GenValidity.GenInvalid b) => Data.GenValidity.GenInvalid (Data.Either.Either a b)
+ Data.GenValidity: instance (Data.GenValidity.GenInvalid a, Data.GenValidity.GenInvalid b) => Data.GenValidity.GenInvalid (a, b)
+ Data.GenValidity: instance (Data.GenValidity.GenInvalid a, Data.GenValidity.GenInvalid b, Data.GenValidity.GenInvalid c) => Data.GenValidity.GenInvalid (a, b, c)
+ Data.GenValidity: instance (Data.GenValidity.GenUnchecked a, Data.GenValidity.GenUnchecked b) => Data.GenValidity.GenUnchecked (Data.Either.Either a b)
+ Data.GenValidity: instance (Data.GenValidity.GenUnchecked a, Data.GenValidity.GenUnchecked b) => Data.GenValidity.GenUnchecked (a, b)
+ Data.GenValidity: instance (Data.GenValidity.GenUnchecked a, Data.GenValidity.GenUnchecked b, Data.GenValidity.GenUnchecked c) => Data.GenValidity.GenUnchecked (a, b, c)
+ Data.GenValidity: instance (Data.GenValidity.GenValid a, Data.GenValidity.GenValid b) => Data.GenValidity.GenValid (Data.Either.Either a b)
+ Data.GenValidity: instance (Data.GenValidity.GenValid a, Data.GenValidity.GenValid b) => Data.GenValidity.GenValid (a, b)
+ Data.GenValidity: instance (Data.GenValidity.GenValid a, Data.GenValidity.GenValid b, Data.GenValidity.GenValid c) => Data.GenValidity.GenValid (a, b, c)
+ Data.GenValidity: instance Data.GenValidity.GenInvalid GHC.Types.Double
+ Data.GenValidity: instance Data.GenValidity.GenInvalid GHC.Types.Float
+ Data.GenValidity: instance Data.GenValidity.GenInvalid a => Data.GenValidity.GenInvalid (GHC.Base.Maybe a)
+ Data.GenValidity: instance Data.GenValidity.GenInvalid a => Data.GenValidity.GenInvalid [a]
+ Data.GenValidity: instance Data.GenValidity.GenUnchecked ()
+ Data.GenValidity: instance Data.GenValidity.GenUnchecked GHC.Integer.Type.Integer
+ Data.GenValidity: instance Data.GenValidity.GenUnchecked GHC.Types.Bool
+ Data.GenValidity: instance Data.GenValidity.GenUnchecked GHC.Types.Char
+ Data.GenValidity: instance Data.GenValidity.GenUnchecked GHC.Types.Double
+ Data.GenValidity: instance Data.GenValidity.GenUnchecked GHC.Types.Float
+ Data.GenValidity: instance Data.GenValidity.GenUnchecked GHC.Types.Int
+ Data.GenValidity: instance Data.GenValidity.GenUnchecked GHC.Types.Ordering
+ Data.GenValidity: instance Data.GenValidity.GenUnchecked GHC.Types.Word
+ Data.GenValidity: instance Data.GenValidity.GenUnchecked a => Data.GenValidity.GenUnchecked (GHC.Base.Maybe a)
+ Data.GenValidity: instance Data.GenValidity.GenUnchecked a => Data.GenValidity.GenUnchecked [a]
+ Data.GenValidity: instance Data.GenValidity.GenValid ()
+ Data.GenValidity: instance Data.GenValidity.GenValid GHC.Integer.Type.Integer
+ Data.GenValidity: instance Data.GenValidity.GenValid GHC.Types.Bool
+ Data.GenValidity: instance Data.GenValidity.GenValid GHC.Types.Char
+ Data.GenValidity: instance Data.GenValidity.GenValid GHC.Types.Double
+ Data.GenValidity: instance Data.GenValidity.GenValid GHC.Types.Float
+ Data.GenValidity: instance Data.GenValidity.GenValid GHC.Types.Int
+ Data.GenValidity: instance Data.GenValidity.GenValid GHC.Types.Ordering
+ Data.GenValidity: instance Data.GenValidity.GenValid GHC.Types.Word
+ Data.GenValidity: instance Data.GenValidity.GenValid a => Data.GenValidity.GenValid (GHC.Base.Maybe a)
+ Data.GenValidity: instance Data.GenValidity.GenValid a => Data.GenValidity.GenValid [a]
- Data.GenRelativeValidity: genInvalidFor :: GenRelativeValidity a b => b -> Gen a
+ Data.GenRelativeValidity: genInvalidFor :: GenRelativeInvalid a b => b -> Gen a
- Data.GenRelativeValidity: genUncheckedFor :: GenRelativeValidity a b => b -> Gen a
+ Data.GenRelativeValidity: genUncheckedFor :: GenRelativeUnchecked a b => b -> Gen a
- Data.GenRelativeValidity: genValidFor :: GenRelativeValidity a b => b -> Gen a
+ Data.GenRelativeValidity: genValidFor :: GenRelativeValid a b => b -> Gen a
- Data.GenValidity: genInvalid :: GenValidity a => Gen a
+ Data.GenValidity: genInvalid :: GenInvalid a => Gen a
- Data.GenValidity: genUnchecked :: GenValidity a => Gen a
+ Data.GenValidity: genUnchecked :: GenUnchecked a => Gen a
- Data.GenValidity: genValid :: GenValidity a => Gen a
+ Data.GenValidity: genValid :: GenValid a => Gen a
Files
- genvalidity.cabal +3/−1
- src/Data/GenRelativeValidity.hs +10/−4
- src/Data/GenValidity.hs +204/−106
genvalidity.cabal view
@@ -1,5 +1,5 @@ name: genvalidity-version: 0.2.0.4+version: 0.3.0.0 synopsis: Testing utilities for the validity library description: Note: There are companion instance packages for this library:@@ -7,6 +7,8 @@ * <https://hackage.haskell.org/package/genvalidity-text genvalidity-text> . * <https://hackage.haskell.org/package/genvalidity-containers genvalidity-containers>+ .+ * <https://hackage.haskell.org/package/genvalidity-path genvalidity-path> homepage: https://github.com/NorfairKing/validity#readme license: MIT
src/Data/GenRelativeValidity.hs view
@@ -1,20 +1,26 @@ {-# LANGUAGE MultiParamTypeClasses #-}+ module Data.GenRelativeValidity ( module Data.RelativeValidity , module Data.GenRelativeValidity ) where -import Data.RelativeValidity-import Data.GenValidity+import Data.GenValidity+import Data.RelativeValidity -import Test.QuickCheck+import Test.QuickCheck -class (GenValidity a, RelativeValidity a b) => GenRelativeValidity a b where+class (GenUnchecked a, RelativeValidity a b) =>+ GenRelativeUnchecked a b where genUncheckedFor :: b -> Gen a genUncheckedFor _ = genUnchecked +class (GenValid a, RelativeValidity a b) =>+ GenRelativeValid a b where genValidFor :: b -> Gen a genValidFor b = genValid `suchThat` (`isValidFor` b) +class (GenUnchecked a, RelativeValidity a b, GenRelativeUnchecked a b) =>+ GenRelativeInvalid a b where genInvalidFor :: b -> Gen a genInvalidFor b = genUncheckedFor b `suchThat` (not . (`isValidFor` b))
src/Data/GenValidity.hs view
@@ -10,21 +10,23 @@ @(Prime <$> arbitrary) `suchThat` isValid@ but this is tedious and inefficient. - The @GenValidity@ type class allows you to specify how to (efficiently)- generate data of the given type to allow for easier and quicker testing.- Just implementing @genUnchecked@ already gives you access to @genValid@ and- @genInvalid@ but writing custom implementations of these functions may speed- up the generation of data.+ The @GenValid@ type class allows you to specify how to (efficiently)+ generate valid data of the given type to allow for easier and quicker testing.+ Just instantiating @GenUnchecked@ already gives you access to a default instance+ of @GenValid@ and @GenInvalid@ but writing custom implementations of these functions+ may speed up the generation of data. For example, to generate primes, we don't have to consider even numbers other than 2. A more efficient implementation could then look as follows: - > instance GenValidity Prime where+ > instance GenUnchecked Prime where > genUnchecked = Prime <$> arbitrary++ > instance GenValid Prime where > genValid = Prime <$> > (oneof > [ pure 2- > , (\y -> 2 * y + 1) <$> (arbitrary `suchThat` (> 0) `suchThat` isPrime)+ > , ((\y -> 2 * abs y + 1) <$> arbitrary) `suchThat` isPrime) > ]) @@ -40,27 +42,28 @@ > Nothing -> return () -- Can happen > Just output -> output `shouldSatisfy` isValid -}- module Data.GenValidity ( module Data.Validity , module Data.GenValidity ) where -import Data.Validity+import Data.Validity -import Test.QuickCheck+import Test.QuickCheck -import Control.Monad (forM)+import Control.Monad (forM) --- | A class of types for which @Validity@-related values can be generated.------ If you also write @Arbitrary@ instances for @GenValidity@ types, it may be--- best to simply write @arbitrary = genValid@.-class Validity a => GenValidity a where- -- | Generate a truly arbitrary datum, this should cover all possible- -- values in the type+-- | A class of types for which truly arbitrary values can be generated.+class GenUnchecked a where genUnchecked :: Gen a +-- | A class of types for which valid values can be generated.+--+-- If you also write @Arbitrary@ instances for @GenValid@ types, it may be+-- best to simply write @arbitrary = genValid@.+class (Validity a, GenUnchecked a) =>+ GenValid a where+ genValid :: Gen a -- | Generate a valid datum, this should cover all possible valid values in -- the type --@@ -71,9 +74,12 @@ -- To speed up testing, it may be a good idea to implement this yourself. -- If you do, make sure that it is possible to generate all possible valid -- data, otherwise your testing may not cover all cases.- genValid :: Gen a genValid = genUnchecked `suchThat` isValid +-- | A class of types for which invalid values can be generated.+class (Validity a, GenUnchecked a) =>+ GenInvalid a where+ genInvalid :: Gen a -- | Generate an invalid datum, this should cover all possible invalid -- values --@@ -82,125 +88,217 @@ -- To speed up testing, it may be a good idea to implement this yourself. -- If you do, make sure that it is possible to generate all possible -- invalid data, otherwise your testing may not cover all cases.- genInvalid :: Gen a genInvalid = genUnchecked `suchThat` (not . isValid)- {-# MINIMAL genUnchecked #-} -instance (GenValidity a, GenValidity b) => GenValidity (a, b) where- genUnchecked = sized $ \n -> do- (r, s) <- genSplit n- a <- resize r genUnchecked- b <- resize s genUnchecked- return (a, b)+instance (GenUnchecked a, GenUnchecked b) =>+ GenUnchecked (a, b) where+ genUnchecked =+ sized $ \n -> do+ (r, s) <- genSplit n+ a <- resize r genUnchecked+ b <- resize s genUnchecked+ return (a, b) - genValid = sized $ \n -> do- (r, s) <- genSplit n- a <- resize r genValid- b <- resize s genValid- return (a, b)+instance (GenValid a, GenValid b) =>+ GenValid (a, b) where+ genValid =+ sized $ \n -> do+ (r, s) <- genSplit n+ a <- resize r genValid+ b <- resize s genValid+ return (a, b) - genInvalid = sized $ \n -> do- (r, s) <- genSplit n- oneof- [ do- a <- resize r genUnchecked- b <- resize s genInvalid- return (a, b)- , do- a <- resize r genInvalid- b <- resize s genUnchecked- return (a, b)- ]+instance (GenInvalid a, GenInvalid b) =>+ GenInvalid (a, b) where+ genInvalid =+ sized $ \n -> do+ (r, s) <- genSplit n+ oneof+ [ do a <- resize r genUnchecked+ b <- resize s genInvalid+ return (a, b)+ , do a <- resize r genInvalid+ b <- resize s genUnchecked+ return (a, b)+ ] -instance (GenValidity a, GenValidity b, GenValidity c) => GenValidity (a, b, c) where- genUnchecked = sized $ \n -> do- (r, s, t) <- genSplit3 n- a <- resize r genUnchecked- b <- resize s genUnchecked- c <- resize t genUnchecked- return (a, b, c)+instance (GenUnchecked a, GenUnchecked b) =>+ GenUnchecked (Either a b) where+ genUnchecked = oneof [Left <$> genUnchecked, Right <$> genUnchecked] - genValid = sized $ \n -> do- (r, s, t) <- genSplit3 n- a <- resize r genValid- b <- resize s genValid- c <- resize t genValid- return (a, b, c)+instance (GenValid a, GenValid b) =>+ GenValid (Either a b) where+ genValid = oneof [Left <$> genValid, Right <$> genValid] - genInvalid = sized $ \n -> do- (r, s, t) <- genSplit3 n- oneof- [ do- a <- resize r genInvalid- b <- resize s genUnchecked- c <- resize t genUnchecked- return (a, b, c)- , do- a <- resize r genUnchecked- b <- resize s genInvalid- c <- resize t genUnchecked- return (a, b, c)- , do- a <- resize r genUnchecked- b <- resize s genUnchecked- c <- resize t genInvalid- return (a, b, c)- ] -instance GenValidity a => GenValidity (Maybe a) where+-- | This instance ensures that the generated tupse contains at least one invalid element. The other element is unchecked.+instance (GenInvalid a, GenInvalid b) =>+ GenInvalid (Either a b) where+ genInvalid = oneof [Left <$> genInvalid, Right <$> genInvalid]++instance (GenUnchecked a, GenUnchecked b, GenUnchecked c) =>+ GenUnchecked (a, b, c) where+ genUnchecked =+ sized $ \n -> do+ (r, s, t) <- genSplit3 n+ a <- resize r genUnchecked+ b <- resize s genUnchecked+ c <- resize t genUnchecked+ return (a, b, c)++instance (GenValid a, GenValid b, GenValid c) =>+ GenValid (a, b, c) where+ genValid =+ sized $ \n -> do+ (r, s, t) <- genSplit3 n+ a <- resize r genValid+ b <- resize s genValid+ c <- resize t genValid+ return (a, b, c)++-- | This instance ensures that the generated triple contains at least one invalid element. The other two are unchecked.+instance (GenInvalid a, GenInvalid b, GenInvalid c) =>+ GenInvalid (a, b, c) where+ genInvalid =+ sized $ \n -> do+ (r, s, t) <- genSplit3 n+ oneof+ [ do a <- resize r genInvalid+ b <- resize s genUnchecked+ c <- resize t genUnchecked+ return (a, b, c)+ , do a <- resize r genUnchecked+ b <- resize s genInvalid+ c <- resize t genUnchecked+ return (a, b, c)+ , do a <- resize r genUnchecked+ b <- resize s genUnchecked+ c <- resize t genInvalid+ return (a, b, c)+ ]++instance GenUnchecked a =>+ GenUnchecked (Maybe a) where genUnchecked = oneof [pure Nothing, Just <$> genUnchecked]- genValid = oneof [pure Nothing, Just <$> genValid]- genInvalid = Just <$> genInvalid +instance GenValid a =>+ GenValid (Maybe a) where+ genValid = oneof [pure Nothing, Just <$> genValid] +instance GenInvalid a =>+ GenInvalid (Maybe a) where+ genInvalid = Just <$> genInvalid++instance GenUnchecked a =>+ GenUnchecked [a] where+ genUnchecked = genListOf genUnchecked+ -- | If we can generate values of a certain type, we can also generate lists of -- them.--- This instance ensures that @genValid@ generates only lists of valid data and--- that @genInvalid@ generates lists of data such that there is at least one--- value in there that does not satisfy @isValid@, the rest is unchecked.-instance GenValidity a => GenValidity [a] where- genUnchecked = genListOf genUnchecked+instance GenValid a =>+ GenValid [a] where+ genValid = genListOf genValid - genValid = genListOf genValid+-- | This instance ensures that the generated list contains at least one element+-- that satisfies 'isInvalid'. The rest is unchecked.+instance GenInvalid a =>+ GenInvalid [a] where+ genInvalid =+ sized $ \n -> do+ (x, y, z) <- genSplit3 n+ before <- resize x $ genListOf genUnchecked+ middle <- resize y genInvalid+ after <- resize z $ genListOf genUnchecked+ return $ before ++ [middle] ++ after - -- | At least one invalid value in the list, the rest could be either.- genInvalid = sized $ \n -> do- (x, y, z) <- genSplit3 n- before <- resize x $ genListOf genUnchecked- middle <- resize y genInvalid- after <- resize z $ genListOf genUnchecked- return $ before ++ [middle] ++ after+instance GenUnchecked () where+ genUnchecked = arbitrary +instance GenValid ()++instance GenUnchecked Bool where+ genUnchecked = arbitrary++instance GenValid Bool++instance GenUnchecked Ordering where+ genUnchecked = arbitrary++instance GenValid Ordering++instance GenUnchecked Char where+ genUnchecked = arbitrary++instance GenValid Char++instance GenUnchecked Int where+ genUnchecked = arbitrary++instance GenValid Int++instance GenUnchecked Word where+ genUnchecked = arbitrary++instance GenValid Word++instance GenUnchecked Float where+ genUnchecked = arbitrary++instance GenValid Float where+ genValid = arbitrary++-- | Either 'NaN' or 'Infinity'.+instance GenInvalid Float where+ genInvalid = elements [read "NaN", read "Infinity"]++instance GenUnchecked Double where+ genUnchecked = arbitrary++instance GenValid Double++-- | Either 'NaN' or 'Infinity'.+instance GenInvalid Double where+ genInvalid = elements [read "NaN", read "Infinity"]++instance GenUnchecked Integer where+ genUnchecked = arbitrary++instance GenValid Integer++-- | 'upTo' generates an integer between 0 (inclusive) and 'n'. upTo :: Int -> Gen Int upTo n | n <= 0 = pure 0 | otherwise = elements [0 .. n] +-- | 'genSplit a' generates a tuple '(b, c)' such that 'b + c' equals 'a'. genSplit :: Int -> Gen (Int, Int) genSplit n- | n < 0 = pure (0, 0)- | otherwise = elements [ (i, n - i) | i <- [0..n] ]+ | n < 0 = pure (0, 0)+ | otherwise = elements [(i, n - i) | i <- [0 .. n]] +-- | 'genSplit a' generates a triple '(b, c, d)' such that 'b + c + d' equals 'a'. genSplit3 :: Int -> Gen (Int, Int, Int) genSplit3 n- | n < 0 = pure (0, 0, 0)+ | n < 0 = pure (0, 0, 0) | otherwise = do- (a, z) <- genSplit n- (b, c) <- genSplit z- return (a, b, c)+ (a, z) <- genSplit n+ (b, c) <- genSplit z+ return (a, b, c) +-- | 'arbPartition n' generates a list 'ls' such that 'sum ls' equals 'n'. arbPartition :: Int -> Gen [Int] arbPartition k | k <= 0 = pure [] | otherwise = do- first <- elements [1..k]- rest <- arbPartition $ k - first- return $ first : rest+ first <- elements [1 .. k]+ rest <- arbPartition $ k - first+ return $ first : rest -- | A version of @listOf@ that takes size into account more accurately. genListOf :: Gen a -> Gen [a]-genListOf func = sized $ \n -> do- size <- upTo n- pars <- arbPartition size- forM pars $ \i -> resize i func-+genListOf func =+ sized $ \n -> do+ size <- upTo n+ pars <- arbPartition size+ forM pars $ \i -> resize i func