genvalidity (empty) → 0.1.0.0
raw patch · 4 files changed
+185/−0 lines, 4 filesdep +QuickCheckdep +basedep +validitysetup-changed
Dependencies added: QuickCheck, base, validity
Files
- LICENSE +21/−0
- Setup.hs +2/−0
- genvalidity.cabal +26/−0
- src/Data/GenValidity.hs +136/−0
+ LICENSE view
@@ -0,0 +1,21 @@+The MIT License (MIT)++Copyright (c) 2016 Tom Sydney Kerckhove++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ genvalidity.cabal view
@@ -0,0 +1,26 @@+name: genvalidity+version: 0.1.0.0+synopsis: Testing utilities for the validity library+description: Please see README.md+homepage: https://github.com/NorfairKing/validity#readme+license: MIT+license-file: LICENSE+author: Tom Sydney Kerckhove+maintainer: syd.kerckhove@gmail.com+copyright: Copyright: (c) 2016 Tom Sydney Kerckhove+category: Testing+build-type: Simple+-- extra-source-files:+cabal-version: >=1.10++library+ hs-source-dirs: src+ exposed-modules: Data.GenValidity+ build-depends: base < 5+ , validity >= 0.1 && < 0.2+ , QuickCheck >= 2.8 && < 2.9+ default-language: Haskell2010++source-repository head+ type: git+ location: https://github.com/NorfairKing/validity
+ src/Data/GenValidity.hs view
@@ -0,0 +1,136 @@+{-|++ @GenValidity@ exists to make tests involving @Validity@ types easier and speed+ up the generation of data for them.++ Let's use the example from @Data.Validity@ again: A datatype that represents+ primes.+ To implement tests for this datatype, we would have to be able primes and+ non-primes. We could do this with @(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.++ 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+ > genUnchecked = Prime <$> arbitrary+ > genValid = Prime <$>+ > (oneof+ > [ pure 2+ > , (\y -> 2 * y + 1) <$> (arbitrary `suchThat` (> 0) `suchThat` isPrime)+ > ])+++ Typical examples of tests involving validity could look as follows:++ > it "succeeds when given valid input" $ do+ > forAll genValid $ \input ->+ > myFunction input `shouldSatisfy` isRight++ > it "produces valid output when it succeeds" $ do+ > forAll genUnchecked $ \input ->+ > case myFunction input of+ > Nothing -> return () -- Can happen+ > Just output -> output `shouldSatisfy` isValid++ -}++module Data.GenValidity+ ( module Data.Validity+ , module Data.GenValidity+ ) where++import Data.Validity++import Test.QuickCheck++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+ genUnchecked :: Gen a++ -- | Generate a valid datum, this should cover all possible valid values in+ -- the type+ --+ -- The default implementation is as follows:+ --+ -- > genValid = genUnchecked `suchThat` isValid+ --+ -- 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++ -- | Generate an invalid datum, this should cover all possible invalid+ -- values+ --+ -- > genInvalid = genUnchecked `suchThat` (not . isValid)+ --+ -- 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 (Maybe a) where+ genUnchecked = oneof [pure Nothing, Just <$> genUnchecked]+ genValid = oneof [pure Nothing, Just <$> genValid]+ genInvalid = Just <$> genInvalid+++-- | 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++ genValid = genListOf genValid++ -- | At least one invalid value in the list, the rest could be either.+ genInvalid = sized $ \n ->+ case n of+ 0 -> (:[]) <$> genInvalid+ 1 -> (:[]) <$> genInvalid+ _ -> do+ (x, y) <- genSplit $ n - 1+ before <- resize x $ genListOf genUnchecked+ middle <- genInvalid+ after <- resize y $ genListOf genUnchecked+ return $ before ++ [middle] ++ after+ where+ genSplit :: Int -> Gen (Int, Int)+ genSplit n = elements $ [ (i, n - i) | i <- [0..n] ]++-- | A version of @listOf@ that takes size into account more accurately.+genListOf :: Gen a -> Gen [a]+genListOf func = sized $ \n ->+ case n of+ 0 -> pure []+ m -> do+ pars <- arbPartition m+ forM pars $ \i -> resize i func+ where+ arbPartition :: Int -> Gen [Int]+ arbPartition 0 = pure []+ arbPartition 1 = pure [1]+ arbPartition k = do+ first <- elements [1..k]+ rest <- arbPartition $ k - first+ return $ first : rest