ImpSpec 0.1.0.0 → 0.2.0.0
raw patch · 5 files changed
+135/−88 lines, 5 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Test.ImpSpec: arbitrary :: (Arbitrary a, MonadGen m) => m a
- Test.ImpSpec: askStatefulGen :: (HasStatefulGen g m, MonadReader g m) => m g
- Test.ImpSpec: assertBool :: (HasCallStack, MonadIO m) => String -> Bool -> m ()
- Test.ImpSpec: assertColorFailure :: (HasCallStack, MonadIO m) => String -> m a
- Test.ImpSpec: assertFailure :: (HasCallStack, MonadIO m) => String -> m a
- Test.ImpSpec: class HasGenEnv env g | env -> g
- Test.ImpSpec: class StatefulGen g m => HasStatefulGen g m | m -> g
- Test.ImpSpec: expectJust :: (HasCallStack, MonadIO m) => Maybe a -> m a
- Test.ImpSpec: expectJustDeep :: (HasCallStack, NFData a, MonadIO m) => Maybe a -> m a
- Test.ImpSpec: expectJustDeep_ :: (HasCallStack, NFData a, MonadIO m) => Maybe a -> m ()
- Test.ImpSpec: expectLeft :: (HasCallStack, Show b, MonadIO m) => Either a b -> m a
- Test.ImpSpec: expectLeftDeep :: (HasCallStack, NFData a, Show b, MonadIO m) => Either a b -> m a
- Test.ImpSpec: expectLeftDeep_ :: (HasCallStack, NFData a, Show b, MonadIO m) => Either a b -> m ()
- Test.ImpSpec: expectNothing :: (HasCallStack, Show a, MonadIO m) => Maybe a -> m ()
- Test.ImpSpec: expectRight :: (HasCallStack, Show a, MonadIO m) => Either a b -> m b
- Test.ImpSpec: expectRightDeep :: (HasCallStack, Show a, NFData b, MonadIO m) => Either a b -> m b
- Test.ImpSpec: expectRightDeep_ :: (HasCallStack, Show a, NFData b, MonadIO m) => Either a b -> m ()
- Test.ImpSpec: expectationFailure :: (HasCallStack, MonadIO m) => String -> m ()
- Test.ImpSpec: getGenEnv :: HasGenEnv env g => env -> g
- Test.ImpSpec: impSpecConfig :: Config
- Test.ImpSpec: impSpecMain :: Spec -> IO ()
- Test.ImpSpec: impSpecMainWithConfig :: Config -> Spec -> IO ()
- Test.ImpSpec: infix 1 `shouldBeLeft`
- Test.ImpSpec: io :: IO a -> IO a
- Test.ImpSpec: shouldBe :: (HasCallStack, MonadIO m, Show a, Eq a) => a -> a -> m ()
- Test.ImpSpec: shouldBeJust :: (HasCallStack, Show a, Eq a, MonadIO m) => Maybe a -> a -> m ()
- Test.ImpSpec: shouldBeLeft :: (HasCallStack, Show a, Eq a, Show b, MonadIO m) => Either a b -> a -> m ()
- Test.ImpSpec: shouldBeRight :: (HasCallStack, Show a, Show b, Eq b, MonadIO m) => Either a b -> b -> m ()
- Test.ImpSpec: shouldContain :: (HasCallStack, MonadIO m, Show a, Eq a) => [a] -> [a] -> m ()
- Test.ImpSpec: shouldEndWith :: (HasCallStack, MonadIO m, Show a, Eq a) => [a] -> [a] -> m ()
- Test.ImpSpec: shouldMatchList :: (HasCallStack, MonadIO m, Show a, Eq a) => [a] -> [a] -> m ()
- Test.ImpSpec: shouldNotBe :: (HasCallStack, MonadIO m, Show a, Eq a) => a -> a -> m ()
- Test.ImpSpec: shouldNotContain :: (HasCallStack, MonadIO m, Show a, Eq a) => [a] -> [a] -> m ()
- Test.ImpSpec: shouldNotReturn :: (HasCallStack, MonadIO m, Show a, Eq a) => m a -> a -> m ()
- Test.ImpSpec: shouldNotSatisfy :: (HasCallStack, MonadIO m, Show a) => a -> (a -> Bool) -> m ()
- Test.ImpSpec: shouldReturn :: (HasCallStack, MonadIO m, Show a, Eq a) => m a -> a -> m ()
- Test.ImpSpec: shouldSatisfy :: (HasCallStack, MonadIO m, Show a) => a -> (a -> Bool) -> m ()
- Test.ImpSpec: shouldStartWith :: (HasCallStack, MonadIO m, Show a, Eq a) => [a] -> [a] -> m ()
- Test.ImpSpec: shouldThrow :: (HasCallStack, Exception e, MonadUnliftIO m) => m a -> Selector e -> m ()
- Test.ImpSpec: type Selector a = a -> Bool
- Test.ImpSpec: uniformByteStringM :: HasStatefulGen a m => Int -> m ByteString
- Test.ImpSpec: uniformListM :: (HasStatefulGen g m, Uniform a) => Int -> m [a]
- Test.ImpSpec: uniformListRM :: (HasStatefulGen g m, UniformRange a) => (a, a) -> Int -> m [a]
- Test.ImpSpec: uniformM :: (HasStatefulGen g m, Uniform a) => m a
- Test.ImpSpec: uniformRM :: (HasStatefulGen g m, UniformRange a) => (a, a) -> m a
- Test.ImpSpec: uniformShortByteStringM :: HasStatefulGen a m => Int -> m ShortByteString
+ Test.ImpSpec: ($dmimpInitIO) :: (ImpSpec t, ImpSpecEnv t ~ Proxy t, ImpSpecState t ~ Proxy t) => QCGen -> IO (ImpInit t)
+ Test.ImpSpec: type family ImpSpecEnv t = (r :: Type) | r -> t
+ Test.ImpSpec.Random: ($dmaskStatefulGen) :: (HasStatefulGen g m, MonadReader g m) => m g
- Test.ImpSpec: impInitIO :: (ImpSpec t, ImpSpecEnv t ~ Proxy t, ImpSpecState t ~ Proxy t) => QCGen -> IO (ImpInit t)
+ Test.ImpSpec: impInitIO :: ImpSpec t => QCGen -> IO (ImpInit t)
- Test.ImpSpec.Random: askStatefulGen :: (HasStatefulGen g m, MonadReader g m) => m g
+ Test.ImpSpec.Random: askStatefulGen :: HasStatefulGen g m => m g
- Test.ImpSpec.Random: class StatefulGen g m => HasStatefulGen g m | m -> g
+ Test.ImpSpec.Random: class StatefulGen g m => HasStatefulGen g (m :: Type -> Type) | m -> g
- Test.ImpSpec.Random: uniformListM :: (HasStatefulGen g m, Uniform a) => Int -> m [a]
+ Test.ImpSpec.Random: uniformListM :: forall a g m. (HasStatefulGen g m, Uniform a) => Int -> m [a]
- Test.ImpSpec.Random: uniformListRM :: (HasStatefulGen g m, UniformRange a) => (a, a) -> Int -> m [a]
+ Test.ImpSpec.Random: uniformListRM :: forall a g m. (HasStatefulGen g m, UniformRange a) => (a, a) -> Int -> m [a]
- Test.ImpSpec.Random: uniformM :: (HasStatefulGen g m, Uniform a) => m a
+ Test.ImpSpec.Random: uniformM :: forall a g m. (HasStatefulGen g m, Uniform a) => m a
- Test.ImpSpec.Random: uniformRM :: (HasStatefulGen g m, UniformRange a) => (a, a) -> m a
+ Test.ImpSpec.Random: uniformRM :: forall a g m. (HasStatefulGen g m, UniformRange a) => (a, a) -> m a
Files
- CHANGELOG.md +5/−1
- ImpSpec.cabal +81/−67
- README.md +4/−4
- src/Test/ImpSpec/Internal.hs +32/−13
- src/Test/ImpSpec/Random.hs +13/−3
CHANGELOG.md view
@@ -1,5 +1,9 @@ # Version history for `ImpSpec` +## 0.2.0.0++* Add explicit `forall` for `uniformM`, `uniformRM`, `uniformListM`, `uniformListRM` and `arbitrary`+ ## 0.1.0.0 -*+* Initital release
ImpSpec.cabal view
@@ -1,79 +1,93 @@-cabal-version: 1.18-name: ImpSpec-version: 0.1.0.0-license: Apache-2.0-license-file: LICENSE-maintainer: operations@iohk.io-author: IOHK-homepage: https://github.com/input-output-hk/ImpSpec+cabal-version: 2.4+name: ImpSpec+version: 0.2.0.0+license: Apache-2.0+license-file: LICENSE+maintainer: operations@iohk.io+author: IOHK+homepage: https://github.com/input-output-hk/ImpSpec synopsis:- Imperative approach to testing stateful applications. ImpSpec is build on top of HSpec and QuickCheck+ Imperative approach to testing stateful applications.+ ImpSpec is built on top of HSpec and QuickCheck.+ description:- Let a little imp help you discover all the bugs in your stateful Haskell program.-category: Control-build-type: Simple-extra-doc-files: CHANGELOG.md+ Let a little imp help you discover all the bugs in your stateful Haskell program.++category: Testing+build-type: Simple+extra-doc-files: CHANGELOG.md extra-source-files: README.md-tested-with: GHC == 8.2.2- , GHC == 8.4.4- , GHC == 8.6.5- , GHC == 8.8.4- , GHC == 8.10.7- , GHC == 9.0.2- , GHC == 9.2.8- , GHC == 9.4.8- , GHC == 9.6.6- , GHC == 9.8.2- , GHC == 9.10.1+tested-with:+ ghc ==8.2.2+ ghc ==8.4.4+ ghc ==8.6.5+ ghc ==8.8.4+ ghc ==8.10.7+ ghc ==9.0.2+ ghc ==9.2.8+ ghc ==9.4.8+ ghc ==9.6.6+ ghc ==9.8.2+ ghc ==9.10.3 source-repository head- type: git- location: https://github.com/intersectmbo/cardano-ledger- subdir: libs/ImpSpec+ type: git+ location: https://github.com/input-output-hk/ImpSpec library- exposed-modules:- Test.ImpSpec- Test.ImpSpec.Expectations- Test.ImpSpec.Expectations.Lifted- Test.ImpSpec.Main- Test.ImpSpec.Random+ exposed-modules:+ Test.ImpSpec+ Test.ImpSpec.Expectations+ Test.ImpSpec.Expectations.Lifted+ Test.ImpSpec.Main+ Test.ImpSpec.Random - hs-source-dirs: src- other-modules: Test.ImpSpec.Internal- default-language: Haskell2010- ghc-options:- -Wall -Wcompat -Wincomplete-record-updates- -Wincomplete-uni-patterns -Wredundant-constraints+ hs-source-dirs: src+ other-modules: Test.ImpSpec.Internal+ default-language: Haskell2010+ ghc-options:+ -Wall+ -Wcompat+ -Wincomplete-record-updates+ -Wincomplete-uni-patterns+ -Wredundant-constraints - build-depends:- base >=4.9 && <5,- bytestring,- deepseq,- hspec,- hspec-core >=2.11,- hspec-expectations-lifted,- HUnit,- mtl,- QuickCheck,- quickcheck-transformer,- prettyprinter >=1.7,- prettyprinter-ansi-terminal >=1.1.2,- random >=1.2,- text,- unliftio+ build-depends:+ HUnit,+ QuickCheck,+ base >=4.9 && <5,+ bytestring,+ deepseq,+ hspec,+ hspec-core >=2.11,+ hspec-expectations-lifted,+ mtl,+ prettyprinter >=1.7,+ prettyprinter-ansi-terminal >=1.1.2,+ quickcheck-transformer,+ random >=1.2,+ text,+ unliftio, test-suite tests- type: exitcode-stdio-1.0- main-is: Main.hs- hs-source-dirs: test- other-modules: Test.Suite.ImpSpec- default-language: Haskell2010- ghc-options:- -Wall -Wcompat -Wincomplete-record-updates- -Wincomplete-uni-patterns -Wredundant-constraints- -threaded -rtsopts+ type: exitcode-stdio-1.0+ main-is: Main.hs+ hs-source-dirs: test+ other-modules: Test.Suite.ImpSpec+ default-language: Haskell2010+ ghc-options:+ -Wall+ -Wcompat+ -Wincomplete-record-updates+ -Wincomplete-uni-patterns+ -Wredundant-constraints+ -threaded+ -rtsopts - build-depends:- base,- ImpSpec+ build-depends:+ ImpSpec,+ base,++source-repository head+ type: git+ location: https://github.com/input-output-hk/ImpSpec
README.md view
@@ -17,10 +17,10 @@ |:--------------:|:---------:|:-------:|:-------:|:---:| | [![Build Status][GA-badge]][GA-link] | [![Coverage Status][Coveralls-badge]][Coveralls-link] | [![Hackage][Hackage-badge]][Hackage-link] | [![Nightly][Nightly-badge]][Nightly-link] | [![LTS][LTS-badge]][LTS-link] -[GA-badge]: https://github.com/lehins/ImpSpec/workflows/CI/badge.svg-[GA-link]: https://github.com/lehins/ImpSpec/actions-[Coveralls-badge]: https://coveralls.io/repos/github/lehins/ImpSpec/badge.svg?branch=master-[Coveralls-link]: https://coveralls.io/github/lehins/ImpSpec?branch=master+[GA-badge]: https://github.com/input-output-hk/ImpSpec/workflows/CI/badge.svg+[GA-link]: https://github.com/input-output-hk/ImpSpec/actions+[Coveralls-badge]: https://coveralls.io/repos/github/input-output-hk/ImpSpec/badge.svg?branch=master+[Coveralls-link]: https://coveralls.io/github/input-output-hk/ImpSpec?branch=master [Hackage-badge]: https://img.shields.io/hackage/v/ImpSpec.svg [Hackage-link]: https://hackage.haskell.org/package/ImpSpec [Nightly-badge]: https://www.stackage.org/package/ImpSpec/badge/nightly
src/Test/ImpSpec/Internal.hs view
@@ -13,6 +13,9 @@ {-# LANGUAGE TypeFamilyDependencies #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-}+#if MIN_VERSION_random(1,3,0) && !MIN_VERSION_QuickCheck(2,16,1)+{-# OPTIONS_GHC -Wno-orphans #-}+#endif module Test.ImpSpec.Internal where @@ -39,7 +42,11 @@ vsep, ) import Prettyprinter.Render.Terminal (AnsiStyle, Color (..), color, renderLazy)-import System.Random (randomR, split)+#if MIN_VERSION_random(1,3,0)+import System.Random (randomR, SplitGen (splitGen))+#else+import System.Random (randomR, RandomGen (split))+#endif import System.Random.Stateful (IOGenM, applyIOGen, newIOGenM) import Test.HUnit.Lang (FailureReason (..), HUnitFailure (..)) import Test.Hspec (Spec, SpecWith, beforeAll, beforeAllWith)@@ -68,6 +75,15 @@ import Data.Monoid ((<>)) #endif +#if !MIN_VERSION_random(1,3,0)+splitGen :: RandomGen g => g -> (g, g)+splitGen = split+#endif++#if MIN_VERSION_random(1,3,0) && !MIN_VERSION_QuickCheck(2,16,1)+deriving instance SplitGen QCGen+#endif+ data ImpState t = ImpState { impStateSpecState :: !(ImpSpecState t) , impStateLog :: !(Doc AnsiStyle)@@ -98,8 +114,11 @@ { impInitEnv :: ImpSpecEnv t , impInitState :: ImpSpecState t }+ deriving instance (Eq (ImpSpecEnv t), Eq (ImpSpecState t)) => Eq (ImpInit t)+ deriving instance (Ord (ImpSpecEnv t), Ord (ImpSpecState t)) => Ord (ImpInit t)+ deriving instance (Show (ImpSpecEnv t), Show (ImpSpecState t)) => Show (ImpInit t) -- | Stores extra information about the failure of the unit test@@ -134,30 +153,30 @@ instance env ~ ImpSpecEnv t => MonadReader env (ImpM t) where ask = impEnvSpecEnv <$> ImpM ask- local f = ImpM . local (\e -> e{impEnvSpecEnv = f (impEnvSpecEnv e)}) . unImpM+ local f = ImpM . local (\e -> e {impEnvSpecEnv = f (impEnvSpecEnv e)}) . unImpM instance Fail.MonadFail (ImpM t) where fail = liftIO . assertFailure instance s ~ ImpSpecState t => MonadState s (ImpM t) where state f = do- ImpEnv{impEnvStateRef} <- ImpM ask+ ImpEnv {impEnvStateRef} <- ImpM ask curState <- readIORef impEnvStateRef let !(result, !newSpecState) = f $ impStateSpecState curState- writeIORef impEnvStateRef (curState{impStateSpecState = newSpecState})+ writeIORef impEnvStateRef (curState {impStateSpecState = newSpecState}) pure result get = fmap impStateSpecState . readIORef . impEnvStateRef =<< ImpM ask instance MonadGen (ImpM t) where liftGen (MkGen f) = do qcSize <- ImpM $ asks impEnvQCSize- qcGen <- applyQCGen split+ qcGen <- applyQCGen splitGen pure $ f qcGen qcSize variant n action = do applyQCGen $ \qcGen -> ((), integerVariant (toInteger n) qcGen) action sized f = ImpM (asks impEnvQCSize) >>= f- resize n (ImpM f) = ImpM $ local (\env -> env{impEnvQCSize = n}) f+ resize n (ImpM f) = ImpM $ local (\env -> env {impEnvQCSize = n}) f choose r = applyQCGen (randomR r) instance HasStatefulGen (IOGenM QCGen) (ImpM t) where@@ -166,7 +185,7 @@ instance (ImpSpec t, Testable a) => Testable (ImpM t a) where property m = property $ MkGen $ \qcGen qcSize -> ioProperty $ do- let (qcGen1, qcGen2) = split qcGen+ let (qcGen1, qcGen2) = splitGen qcGen impInit <- impInitIO qcGen1 evalImpM (Just qcGen2) (Just qcSize) impInit m @@ -186,10 +205,10 @@ (r, testable, logs) <- evalImpM (fst <$> mQC) (snd <$> mQC) impInit $ do t <- impTest x qcSize <- ImpM $ asks impEnvQCSize- qcGen <- applyQCGen split+ qcGen <- applyQCGen splitGen logs <- getLogs pure (Just (qcGen, qcSize), t, logs)- let params' = params{paramsQuickCheckArgs = args{replay = r, chatty = False}}+ let params' = params {paramsQuickCheckArgs = args {replay = r, chatty = False}} res <- evaluateExample (counterexample (ansiDocToString logs) testable)@@ -212,11 +231,11 @@ modifyLogs :: (Doc AnsiStyle -> Doc AnsiStyle) -> ImpM t () modifyLogs f = do ref <- ImpM $ asks impEnvStateRef- modifyIORef ref $ \s -> s{impStateLog = f (impStateLog s)}+ modifyIORef ref $ \s -> s {impStateLog = f (impStateLog s)} -- | Override the QuickCheck generator using a fixed seed. impSetSeed :: Int -> ImpM t ()-impSetSeed seed = applyQCGen $ \_ -> ((), mkQCGen seed)+impSetSeed seed = applyQCGen $ const ((), mkQCGen seed) evalImpGenM :: ImpSpec t => ImpInit t -> ImpM t b -> Gen (IO b) evalImpGenM impInit = fmap (fmap fst) . runImpGenM impInit@@ -253,7 +272,7 @@ ImpInit t -> ImpM t b -> IO (b, ImpState t)-runImpM mQCGen mQCSize ImpInit{impInitEnv, impInitState} action = do+runImpM mQCGen mQCSize ImpInit {impInitEnv, impInitState} action = do let qcSize = fromMaybe 30 mQCSize qcGen = fromMaybe (mkQCGen 2024) mQCGen ioRef <-@@ -364,7 +383,7 @@ logWithCallStack callStack entry = modifyLogs (<> stack <> line <> indent 2 entry <> line) where- prettySrcLoc' SrcLoc{srcLocModule, srcLocStartLine} =+ prettySrcLoc' SrcLoc {srcLocModule, srcLocStartLine} = hcat [ annotate (color c) d | (c, d) <-
src/Test/ImpSpec/Random.hs view
@@ -1,9 +1,9 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE DefaultSignatures #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FunctionalDependencies #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} @@ -35,6 +35,7 @@ askStatefulGen = ReaderT (pure . getGenEnv) uniformM ::+ forall a g m. ( HasStatefulGen g m , R.Uniform a ) =>@@ -43,6 +44,7 @@ {-# INLINE uniformM #-} uniformRM ::+ forall a g m. ( HasStatefulGen g m , R.UniformRange a ) =>@@ -52,6 +54,7 @@ {-# INLINE uniformRM #-} uniformListM ::+ forall a g m. ( HasStatefulGen g m , R.Uniform a ) =>@@ -61,6 +64,7 @@ {-# INLINE uniformListM #-} uniformListRM ::+ forall a g m. (HasStatefulGen g m, R.UniformRange a) => (a, a) -> Int ->@@ -73,9 +77,15 @@ {-# INLINE uniformByteStringM #-} uniformShortByteStringM :: HasStatefulGen a m => Int -> m ShortByteString+#if MIN_VERSION_random(1,3,0)+uniformShortByteStringM n = askStatefulGen >>= R.uniformShortByteStringM n+#else uniformShortByteStringM n = askStatefulGen >>= R.uniformShortByteString n+#endif {-# INLINE uniformShortByteStringM #-} -- | Lifted version of `QC.arbitrary`.-arbitrary :: (QC.Arbitrary a, MonadGen m) => m a+arbitrary ::+ forall a m.+ (QC.Arbitrary a, MonadGen m) => m a arbitrary = liftGen QC.arbitrary