antigen 0.4.0.0 → 0.5.0.0
raw patch · 8 files changed
+197/−77 lines, 8 filesdep +mtlPVP ok
version bump matches the API change (PVP)
Dependencies added: mtl
API changes (from Hackage documentation)
- Test.AntiGen.Internal: (#!) :: AntiGen a -> Text -> AntiGen a
- Test.AntiGen.Internal: (|!) :: Gen a -> Gen a -> AntiGen a
- Test.AntiGen.Internal: infixl 5 #!
- Test.AntiGen.Internal: infixl 6 |!
- Test.AntiGen.Internal: reweigh :: Float -> AntiGen a -> AntiGen a
- Test.AntiGen.Internal: scaleWeight :: (Float -> Float) -> AntiGen a -> AntiGen a
+ Test.AntiGen: annotate :: MonadAntiGen m => Text -> m a -> m a
+ Test.AntiGen: class Monad m => MonadAntiGen (m :: Type -> Type)
+ Test.AntiGen: liftAntiGen :: MonadAntiGen m => AntiGen a -> m a
+ Test.AntiGen: mapAntiGen :: MonadAntiGen m => (forall x. () => AntiGen x -> AntiGen x) -> m a -> m a
+ Test.AntiGen.Class: (#!) :: MonadAntiGen m => m a -> Text -> m a
+ Test.AntiGen.Class: (|!) :: MonadAntiGen m => Gen a -> Gen a -> m a
+ Test.AntiGen.Class: annotate :: MonadAntiGen m => Text -> m a -> m a
+ Test.AntiGen.Class: class Monad m => MonadAntiGen (m :: Type -> Type)
+ Test.AntiGen.Class: infixl 5 #!
+ Test.AntiGen.Class: infixl 6 |!
+ Test.AntiGen.Class: instance (Test.AntiGen.Class.MonadAntiGen m, GHC.Base.Monoid e) => Test.AntiGen.Class.MonadAntiGen (Control.Monad.Trans.Writer.CPS.WriterT e m)
+ Test.AntiGen.Class: instance (Test.AntiGen.Class.MonadAntiGen m, GHC.Base.Monoid e) => Test.AntiGen.Class.MonadAntiGen (Control.Monad.Trans.Writer.Lazy.WriterT e m)
+ Test.AntiGen.Class: instance (Test.AntiGen.Class.MonadAntiGen m, GHC.Base.Monoid e) => Test.AntiGen.Class.MonadAntiGen (Control.Monad.Trans.Writer.Strict.WriterT e m)
+ Test.AntiGen.Class: instance (Test.AntiGen.Class.MonadAntiGen m, GHC.Base.Monoid w) => Test.AntiGen.Class.MonadAntiGen (Control.Monad.Trans.RWS.CPS.RWST r w s m)
+ Test.AntiGen.Class: instance (Test.AntiGen.Class.MonadAntiGen m, GHC.Base.Monoid w) => Test.AntiGen.Class.MonadAntiGen (Control.Monad.Trans.RWS.Lazy.RWST r w s m)
+ Test.AntiGen.Class: instance (Test.AntiGen.Class.MonadAntiGen m, GHC.Base.Monoid w) => Test.AntiGen.Class.MonadAntiGen (Control.Monad.Trans.RWS.Strict.RWST r w s m)
+ Test.AntiGen.Class: instance Test.AntiGen.Class.MonadAntiGen Test.AntiGen.Internal.AntiGen
+ Test.AntiGen.Class: instance Test.AntiGen.Class.MonadAntiGen m => Test.AntiGen.Class.MonadAntiGen (Control.Monad.Trans.Except.ExceptT e m)
+ Test.AntiGen.Class: instance Test.AntiGen.Class.MonadAntiGen m => Test.AntiGen.Class.MonadAntiGen (Control.Monad.Trans.Identity.IdentityT m)
+ Test.AntiGen.Class: instance Test.AntiGen.Class.MonadAntiGen m => Test.AntiGen.Class.MonadAntiGen (Control.Monad.Trans.Reader.ReaderT e m)
+ Test.AntiGen.Class: instance Test.AntiGen.Class.MonadAntiGen m => Test.AntiGen.Class.MonadAntiGen (Control.Monad.Trans.State.Lazy.StateT e m)
+ Test.AntiGen.Class: instance Test.AntiGen.Class.MonadAntiGen m => Test.AntiGen.Class.MonadAntiGen (Control.Monad.Trans.State.Strict.StateT e m)
+ Test.AntiGen.Class: liftAntiGen :: MonadAntiGen m => AntiGen a -> m a
+ Test.AntiGen.Class: mapAntiGen :: MonadAntiGen m => (forall x. () => AntiGen x -> AntiGen x) -> m a -> m a
+ Test.AntiGen.Class: reweigh :: MonadAntiGen m => Float -> m a -> m a
+ Test.AntiGen.Class: scaleWeight :: MonadAntiGen m => (Float -> Float) -> m a -> m a
+ Test.AntiGen.Internal: mkAntiGen :: Gen a -> Gen a -> AntiGen a
+ Test.AntiGen.Internal: withScaledWeight :: (Float -> Float) -> AntiGen a -> AntiGen a
- Test.AntiGen: (#!) :: AntiGen a -> Text -> AntiGen a
+ Test.AntiGen: (#!) :: MonadAntiGen m => m a -> Text -> m a
- Test.AntiGen: (|!) :: Gen a -> Gen a -> AntiGen a
+ Test.AntiGen: (|!) :: MonadAntiGen m => Gen a -> Gen a -> m a
- Test.AntiGen: (||!) :: AntiGen a -> AntiGen a -> AntiGen a
+ Test.AntiGen: (||!) :: MonadAntiGen m => m a -> m a -> m a
- Test.AntiGen: antiChoose :: (Integral a, Random a) => (a, a) -> (a, a) -> AntiGen a
+ Test.AntiGen: antiChoose :: (Integral a, Random a, MonadAntiGen m, MonadGen m) => (a, a) -> (a, a) -> m a
- Test.AntiGen: antiChooseBounded :: (Integral a, Random a, Bounded a) => (a, a) -> AntiGen a
+ Test.AntiGen: antiChooseBounded :: (Integral a, Random a, Bounded a, MonadAntiGen m, MonadGen m) => (a, a) -> m a
- Test.AntiGen: antiDistinctPair :: (Num a, Arbitrary a, Eq a) => AntiGen (a, a)
+ Test.AntiGen: antiDistinctPair :: (Num a, Arbitrary a, Eq a, MonadAntiGen m) => m (a, a)
- Test.AntiGen: antiJust :: a -> AntiGen (Maybe a)
+ Test.AntiGen: antiJust :: MonadAntiGen m => a -> m (Maybe a)
- Test.AntiGen: antiNegative :: (Num a, Ord a, Arbitrary a) => AntiGen a
+ Test.AntiGen: antiNegative :: (Num a, Ord a, Arbitrary a, MonadAntiGen m) => m a
- Test.AntiGen: antiNonEmpty :: AntiGen a -> AntiGen [a]
+ Test.AntiGen: antiNonEmpty :: (MonadGen m, MonadAntiGen m) => m a -> m [a]
- Test.AntiGen: antiNonNegative :: (Num a, Ord a, Arbitrary a) => AntiGen a
+ Test.AntiGen: antiNonNegative :: (Num a, Ord a, Arbitrary a, MonadAntiGen m) => m a
- Test.AntiGen: antiNonPositive :: (Num a, Ord a, Arbitrary a) => AntiGen a
+ Test.AntiGen: antiNonPositive :: (Num a, Ord a, Arbitrary a, MonadAntiGen m) => m a
- Test.AntiGen: antiPositive :: (Num a, Ord a, Arbitrary a) => AntiGen a
+ Test.AntiGen: antiPositive :: (Num a, Ord a, Arbitrary a, MonadAntiGen m) => m a
- Test.AntiGen: antiSamePair :: (Arbitrary a, Num a, Eq a) => AntiGen (a, a)
+ Test.AntiGen: antiSamePair :: (Arbitrary a, Num a, Eq a, MonadAntiGen m) => m (a, a)
- Test.AntiGen: faultyBool :: Bool -> AntiGen Bool
+ Test.AntiGen: faultyBool :: MonadAntiGen m => Bool -> m Bool
- Test.AntiGen: faultyNum :: (Eq a, Num a, Arbitrary a) => a -> AntiGen a
+ Test.AntiGen: faultyNum :: (Eq a, Num a, Arbitrary a, MonadAntiGen m) => a -> m a
- Test.AntiGen: faultyNumRange :: (Random a, Eq a) => a -> (a, a) -> AntiGen a
+ Test.AntiGen: faultyNumRange :: (Random a, Eq a, MonadAntiGen m) => a -> (a, a) -> m a
- Test.AntiGen: faultyTry :: (Eq a, Arbitrary a) => a -> AntiGen a
+ Test.AntiGen: faultyTry :: (Eq a, Arbitrary a, MonadGen m, MonadAntiGen m) => a -> m a
- Test.AntiGen: faultyTryGen :: Eq a => a -> AntiGen a -> AntiGen a
+ Test.AntiGen: faultyTryGen :: (Eq a, MonadAntiGen m, MonadGen m) => a -> m a -> m a
- Test.AntiGen: replicateMNorm :: Int -> AntiGen a -> AntiGen [a]
+ Test.AntiGen: replicateMNorm :: MonadAntiGen m => Int -> m a -> m [a]
- Test.AntiGen: reweigh :: Float -> AntiGen a -> AntiGen a
+ Test.AntiGen: reweigh :: MonadAntiGen m => Float -> m a -> m a
- Test.AntiGen: scaleWeight :: (Float -> Float) -> AntiGen a -> AntiGen a
+ Test.AntiGen: scaleWeight :: MonadAntiGen m => (Float -> Float) -> m a -> m a
Files
- CHANGELOG.md +6/−0
- README.md +46/−24
- antigen.cabal +3/−1
- bench/Main.hs +1/−1
- src/Test/AntiGen.hs +29/−23
- src/Test/AntiGen/Class.hs +107/−0
- src/Test/AntiGen/Internal.hs +4/−27
- test/Main.hs +1/−1
CHANGELOG.md view
@@ -1,5 +1,11 @@ # Revision history for antigen +## 0.5.0.0++* Add `Test.AntiGen.Class`+* Add `MonadAntiGen`+* Make all the helper functions work with abstract monads instead of `AntiGen`+ ## 0.4.0.0 * Add `antiSamePair`
README.md view
@@ -1,39 +1,61 @@ # AntiGen -AntiGen lets you write `QuickCheck` generators that can also be negated to generate negative examples. It can be used as a drop-in replacement for `Gen`.+AntiGen lets you write `QuickCheck` generators that can also produce *negative* examples — subtly malformed variants of the valid data. It's a drop-in replacement for `Gen`. +## Why?++Property-based tests usually check that valid inputs are accepted. But validators and parsers also need to reject malformed inputs, and writing a second generator for the invalid case duplicates logic and tends to drift out of sync with the positive one.++With AntiGen you describe the valid shape once, marking each constrained choice with what an out-of-bounds variant would look like. The same generator can then produce either a valid example or a negative example with `n` of those choices flipped — and any later decisions that depend on an earlier choice see the flipped value, so the rest of the structure cascades accordingly.+ ## Example +Consider validating a length-prefixed message: a declared length field followed by a payload that must have exactly that many characters, each of which must be an `'a'`:+ ```haskell--- Returns an integer `n` (such that 0 <= n <= 5) and a string of length `n` consisting only of characters 'a'-antiGenLengthString :: AntiGen (Int, String)-antiGenLengthString = do- -- Use (|!) to provide both a positive and a negative generator- l <- choose (0, 5) |! choose (6, 10)- s <-- pure (replicate l 'a') |! do- NonNegative l' <- suchThat arbitrary $ \(NonNegative x) -> x /= l- pure $ replicate l' 'b'- pure (l, s)+data Message = Message { declaredLen :: Int, payload :: String } deriving Show++isValid :: Message -> Bool+isValid (Message n s) =+ 0 <= n && n <= 10 && length s == n && all (== 'a') s++messageGen :: AntiGen Message+messageGen = do+ n <- choose (0, 10) |! choose (11, 20) -- declared length+ nElems <- pure n |! choose (n + 1, n + 10) -- actual number of elements+ s <- replicateM nElems (pure 'a' |! elements ['b' .. 'z'])+ pure (Message n s) ``` -To generate a positive example, use `runAntiGen`+Use `runAntiGen` to draw a valid example:+ ```-ghci> generate (runAntiGen antiGenLengthString)-(1, "a")+ghci> generate (runAntiGen messageGen)+Message {declaredLen = 5, payload = "aaaaa"} ``` -To generate a negative example, use `zapAntiGen`-```-ghci> generate (zapAntiGen 1 antiGenLengthString)-(6, "aaaaaa") -- length is too long-ghci> generate (zapAntiGen 1 antiGenLengthString)-(2, "bbbb") -- length of the string does not match up with the integer+Use `zapAntiGen k` to draw a negative example by flipping at most `k` of the decision points:+ ```+ghci> generate (zapAntiGen 1 messageGen)+Message {declaredLen = 4, payload = "aaha"}+-- one character was corrupted; the length field and payload length still agree -Notice that there is exactly one mistake in the example above. -The first argument of `zapAntiGen` can be used to specify how many negations the generator should introduce.+ghci> generate (zapAntiGen 1 messageGen)+Message {declaredLen = 17, payload = "aaaaaaaaaaaaaaaaa"}+-- the length field was flipped out of range; the payload cascaded to still+-- match it — and with 17 characters, there are now 17 character-level+-- decision points rather than the original few++ghci> generate (zapAntiGen 3 messageGen)+Message {declaredLen = 15, payload = "aaaaaaaaaadaaaaaa"}+-- three mistakes:+-- length out of range, one character corrupted, declared length does not match actual length (15 vs 17) ```-ghci> generate $ zapAntiGen 2 antiGenLengthString-(10,"b") -- both values are wrong++The same generator now drives both directions of the validator's property:++```haskell+prop_accepts = forAll (runAntiGen messageGen) isValid+prop_rejects = forAll (zapAntiGen 1 messageGen) (not . isValid) ```
antigen.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: antigen-version: 0.4.0.0+version: 0.5.0.0 synopsis: Fault injection for QuickCheck description: AntiGen extends QuickCheck to allow injecting random faults into QuickCheck @@ -30,6 +30,7 @@ import: warnings exposed-modules: Test.AntiGen+ Test.AntiGen.Class Test.AntiGen.Internal build-depends: base >=4.18 && <5,@@ -39,6 +40,7 @@ random >= 1.2 && < 1.4, text >= 2.0 && < 2.2, quickcheck-transformer >= 0.3.1 && < 0.4,+ mtl >= 2.3.1 && < 2.4, hs-source-dirs: src default-language: Haskell2010
bench/Main.hs view
@@ -7,6 +7,7 @@ import Control.DeepSeq (deepseq) import Criterion.Main (Benchmarkable, bench, bgroup, defaultMain, nfIO) import qualified Data.Text as T+import Test.AntiGen ((|!)) import Test.AntiGen.Internal ( AntiGen, ZapResult (..),@@ -14,7 +15,6 @@ evalToPartial, withAnnotation, zapAt,- (|!), ) import Test.QuickCheck (Arbitrary (..), generate) import Test.QuickCheck.GenT (MonadGen (..))
src/Test/AntiGen.hs view
@@ -12,6 +12,7 @@ module Test.AntiGen ( AntiGen, ZapResult (..),+ MonadAntiGen (..), (|!), (#!), (||!),@@ -20,6 +21,7 @@ zapAntiGen, zapAntiGenResult, prettyZapResult,+ annotate, scaleWeight, reweigh, @@ -46,18 +48,15 @@ import Control.Monad (join, replicateM) import System.Random (Random)+import Test.AntiGen.Class (MonadAntiGen (..), annotate, reweigh, scaleWeight, (#!), (|!)) import Test.AntiGen.Internal ( AntiGen, ZapResult (..), prettyZapResult,- reweigh, runAntiGen,- scaleWeight, withAnnotation, zapAntiGen, zapAntiGenResult,- (#!),- (|!), ) import Test.QuickCheck ( Arbitrary (..),@@ -72,22 +71,22 @@ -- | Returns the provided number. -- -- Negative: returns a value that is not equal to the provided number.-faultyNum :: (Eq a, Num a, Arbitrary a) => a -> AntiGen a+faultyNum :: (Eq a, Num a, Arbitrary a, MonadAntiGen m) => a -> m a faultyNum n = pure n |! ((n +) . getNonZero <$> arbitrary) -faultyNumRange :: (Random a, Eq a) => a -> (a, a) -> AntiGen a+faultyNumRange :: (Random a, Eq a, MonadAntiGen m) => a -> (a, a) -> m a faultyNumRange n rng = pure n |! (choose rng `suchThat` (/= n)) -- | Returns the provided `Bool`. -- -- Negative: returns the negation of that `Bool`.-faultyBool :: Bool -> AntiGen Bool+faultyBool :: MonadAntiGen m => Bool -> m Bool faultyBool b = pure b |! pure (not b) -- | Generates a value from the first range. -- -- Negative: Generates a value from the second range excluding the first range.-antiChoose :: (Integral a, Random a) => (a, a) -> (a, a) -> AntiGen a+antiChoose :: (Integral a, Random a, MonadAntiGen m, MonadGen m) => (a, a) -> (a, a) -> m a antiChoose rng@(lo, hi) (boundLo, boundHi) | lo > boundLo || boundHi > hi = choose rng@@ -102,7 +101,14 @@ -- -- Negative: Returns a random value outside the range between `minBound` and -- `maxBound`.-antiChooseBounded :: (Integral a, Random a, Bounded a) => (a, a) -> AntiGen a+antiChooseBounded ::+ ( Integral a+ , Random a+ , Bounded a+ , MonadAntiGen m+ , MonadGen m+ ) =>+ (a, a) -> m a antiChooseBounded rng = antiChoose rng (minBound, maxBound) -- | Returns the provided value@@ -112,7 +118,7 @@ -- -- Warning: It uses `suchThat`, so using it on small types might end up -- discarding many values.-faultyTry :: (Eq a, Arbitrary a) => a -> AntiGen a+faultyTry :: (Eq a, Arbitrary a, MonadGen m, MonadAntiGen m) => a -> m a faultyTry a = faultyTryGen a $ liftGen arbitrary -- | Returns the provided value@@ -122,49 +128,49 @@ -- -- Warning: It uses `suchThat`, so using it on small types might end up -- discarding many values.-faultyTryGen :: Eq a => a -> AntiGen a -> AntiGen a+faultyTryGen :: (Eq a, MonadAntiGen m, MonadGen m) => a -> m a -> m a faultyTryGen a gen = pure a ||! (gen `suchThat` (/= a)) -- | Returns a positive number -- -- Negative: Returns a non-positive number-antiPositive :: (Num a, Ord a, Arbitrary a) => AntiGen a+antiPositive :: (Num a, Ord a, Arbitrary a, MonadAntiGen m) => m a antiPositive = (getPositive <$> arbitrary) |! (getNonPositive <$> arbitrary) -- | Returns a non-positive number -- -- Negative: Returns a positive number-antiNonPositive :: (Num a, Ord a, Arbitrary a) => AntiGen a+antiNonPositive :: (Num a, Ord a, Arbitrary a, MonadAntiGen m) => m a antiNonPositive = (getNonPositive <$> arbitrary) |! (getPositive <$> arbitrary) -- | Returns a negative number -- -- Negative: Returns a non-negative number-antiNegative :: (Num a, Ord a, Arbitrary a) => AntiGen a+antiNegative :: (Num a, Ord a, Arbitrary a, MonadAntiGen m) => m a antiNegative = (getNegative <$> arbitrary) |! (getNonNegative <$> arbitrary) -- | Returns a non-negative number -- -- Negative: Returns a negative number-antiNonNegative :: (Num a, Ord a, Arbitrary a) => AntiGen a+antiNonNegative :: (Num a, Ord a, Arbitrary a, MonadAntiGen m) => m a antiNonNegative = (getNonNegative <$> arbitrary) |! (getNegative <$> arbitrary) -- | Returns `Just x` -- -- Negative: Returns `Nothing`-antiJust :: a -> AntiGen (Maybe a)+antiJust :: MonadAntiGen m => a -> m (Maybe a) antiJust x = pure (Just x) ||! pure Nothing -- | Returns a non-empty list -- -- Negative: Generate an empty list-antiNonEmpty :: AntiGen a -> AntiGen [a]+antiNonEmpty :: (MonadGen m, MonadAntiGen m) => m a -> m [a] antiNonEmpty x = listOf1 x ||! pure [] -- | Generate a pair with equal values -- -- Negative: Generates a pair of distinct values-antiSamePair :: (Arbitrary a, Num a, Eq a) => AntiGen (a, a)+antiSamePair :: (Arbitrary a, Num a, Eq a, MonadAntiGen m) => m (a, a) antiSamePair = ((\x -> (x, x)) <$> arbitrary) |! ( do@@ -176,7 +182,7 @@ -- | Generates a pair (x, y) where x /= y. -- -- Negative: Generates a pair (x, y) where x == y.-antiDistinctPair :: (Num a, Arbitrary a, Eq a) => AntiGen (a, a)+antiDistinctPair :: (Num a, Arbitrary a, Eq a, MonadAntiGen m) => m (a, a) antiDistinctPair = ( do x <- arbitrary@@ -189,15 +195,15 @@ ) -- | Create an `AntiGen` from a positive and a negative `AntiGen` generator-(||!) :: AntiGen a -> AntiGen a -> AntiGen a+(||!) :: MonadAntiGen m => m a -> m a -> m a a ||! b = join $ pure a |! pure b +infixl 6 ||!+ -- | Like 'replicateM', but normalizes the weight of each element by @1\/n@. -- -- The total weight of the list becomes the average weight of its elements, -- rather than the sum. This prevents longer lists from having a -- disproportionately higher chance of being zapped.-replicateMNorm :: Int -> AntiGen a -> AntiGen [a]+replicateMNorm :: MonadAntiGen m => Int -> m a -> m [a] replicateMNorm n = replicateM n . scaleWeight (/ fromIntegral n)--infixl 6 ||!
+ src/Test/AntiGen/Class.hs view
@@ -0,0 +1,107 @@+{-# LANGUAGE RankNTypes #-}++module Test.AntiGen.Class (+ MonadAntiGen (..),+ annotate,+ reweigh,+ scaleWeight,+ (|!),+ (#!),+) where++import Control.Monad.Except (ExceptT, mapExceptT)+import Control.Monad.Identity (IdentityT, mapIdentityT)+import qualified Control.Monad.RWS.CPS as CPSRWS+import qualified Control.Monad.RWS.Lazy as LazyRWS+import qualified Control.Monad.RWS.Strict as StrictRWS+import Control.Monad.Reader (ReaderT, mapReaderT)+import qualified Control.Monad.State.Lazy as Lazy+import qualified Control.Monad.State.Strict as Strict+import Control.Monad.Trans (MonadTrans (..))+import qualified Control.Monad.Writer.CPS as CPS+import qualified Control.Monad.Writer.Lazy as Lazy+import qualified Control.Monad.Writer.Strict as Strict+import Data.Text (Text)+import Test.AntiGen.Internal+import Test.QuickCheck.Gen (Gen)++class Monad m => MonadAntiGen m where+ liftAntiGen :: AntiGen a -> m a+ mapAntiGen :: (forall x. AntiGen x -> AntiGen x) -> m a -> m a++annotate :: MonadAntiGen m => Text -> m a -> m a+annotate m = mapAntiGen $ withAnnotation m++scaleWeight :: MonadAntiGen m => (Float -> Float) -> m a -> m a+scaleWeight f = mapAntiGen $ withScaledWeight f++-- | Create a negatable generator by providing a positive and a negative+-- generator+(|!) :: MonadAntiGen m => Gen a -> Gen a -> m a+p |! n = liftAntiGen $ mkAntiGen p n++infixl 6 |!++instance MonadAntiGen AntiGen where+ liftAntiGen = id+ mapAntiGen f = f++reweigh :: MonadAntiGen m => Float -> m a -> m a+reweigh w = scaleWeight $ const w++-- | Postfix annotation operator. Annotates an 'AntiGen' with a label that+-- will be included in 'ZapResult' when this generator is zapped.+--+-- @+-- myGen = positive |! negative #! "sign"+-- @+(#!) :: MonadAntiGen m => m a -> Text -> m a+(#!) = flip annotate++infixl 5 #!++-- # MTL instances++instance MonadAntiGen m => MonadAntiGen (ExceptT e m) where+ liftAntiGen = lift . liftAntiGen+ mapAntiGen f = mapExceptT $ mapAntiGen f++instance MonadAntiGen m => MonadAntiGen (IdentityT m) where+ liftAntiGen = lift . liftAntiGen+ mapAntiGen f = mapIdentityT $ mapAntiGen f++instance MonadAntiGen m => MonadAntiGen (Lazy.StateT e m) where+ liftAntiGen = lift . liftAntiGen+ mapAntiGen f = Lazy.mapStateT $ mapAntiGen f++instance MonadAntiGen m => MonadAntiGen (Strict.StateT e m) where+ liftAntiGen = lift . liftAntiGen+ mapAntiGen f = Strict.mapStateT $ mapAntiGen f++instance (MonadAntiGen m, Monoid e) => MonadAntiGen (Lazy.WriterT e m) where+ liftAntiGen = lift . liftAntiGen+ mapAntiGen f = Lazy.mapWriterT $ mapAntiGen f++instance (MonadAntiGen m, Monoid e) => MonadAntiGen (Strict.WriterT e m) where+ liftAntiGen = lift . liftAntiGen+ mapAntiGen f = Strict.mapWriterT $ mapAntiGen f++instance (MonadAntiGen m, Monoid e) => MonadAntiGen (CPS.WriterT e m) where+ liftAntiGen = lift . liftAntiGen+ mapAntiGen f = CPS.mapWriterT $ mapAntiGen f++instance MonadAntiGen m => MonadAntiGen (ReaderT e m) where+ liftAntiGen = lift . liftAntiGen+ mapAntiGen f = mapReaderT $ mapAntiGen f++instance (MonadAntiGen m, Monoid w) => MonadAntiGen (LazyRWS.RWST r w s m) where+ liftAntiGen = lift . liftAntiGen+ mapAntiGen f = LazyRWS.mapRWST $ mapAntiGen f++instance (MonadAntiGen m, Monoid w) => MonadAntiGen (StrictRWS.RWST r w s m) where+ liftAntiGen = lift . liftAntiGen+ mapAntiGen f = StrictRWS.mapRWST $ mapAntiGen f++instance (MonadAntiGen m, Monoid w) => MonadAntiGen (CPSRWS.RWST r w s m) where+ liftAntiGen = lift . liftAntiGen+ mapAntiGen f = CPSRWS.mapRWST $ mapAntiGen f
src/Test/AntiGen/Internal.hs view
@@ -14,8 +14,7 @@ AntiGen, ZapResult (..), prettyZapResult,- (|!),- (#!),+ mkAntiGen, zapAntiGen, zapAntiGenResult, runAntiGen,@@ -24,8 +23,7 @@ countDecisionPoints, zapAt, withAnnotation,- scaleWeight,- reweigh,+ withScaledWeight, ) where import Control.Monad ((<=<))@@ -82,33 +80,12 @@ mkAntiGen active alt = AntiGen $ F $ \p b -> b $ BiGen (p <$> active) (Just $ p <$> alt) 1 id --- | Create a negatable generator by providing a positive and a negative--- generator-(|!) :: Gen a -> Gen a -> AntiGen a-(|!) = mkAntiGen--infixl 6 |!---- | Postfix annotation operator. Annotates an 'AntiGen' with a label that--- will be included in 'ZapResult' when this generator is zapped.------ @--- myGen = positive |! negative #! "sign"--- @-(#!) :: AntiGen a -> Text -> AntiGen a-(#!) = flip withAnnotation--infixl 5 #!- -- | Wrap an AntiGen with an annotation withAnnotation :: Text -> AntiGen a -> AntiGen a withAnnotation ann inner = wrap $ Annotate ann inner pure -scaleWeight :: (Float -> Float) -> AntiGen a -> AntiGen a-scaleWeight rw inner = wrap $ Reweigh rw inner pure--reweigh :: Float -> AntiGen a -> AntiGen a-reweigh w = scaleWeight $ const w+withScaledWeight :: (Float -> Float) -> AntiGen a -> AntiGen a+withScaledWeight rw inner = wrap $ Reweigh rw inner pure data DecisionPoint next where DecisionPoint ::
test/Main.hs view
@@ -26,6 +26,7 @@ faultyNum, faultyTry, replicateMNorm,+ reweigh, runAntiGen, zapAntiGen, (|!),@@ -36,7 +37,6 @@ countDecisionPoints, evalToPartial, prettyZapResult,- reweigh, withAnnotation, zapAntiGenResult, )