packages feed

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 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