hspec-meta 2.11.13 → 2.11.14
raw patch · 10 files changed
+263/−111 lines, 10 filesdep ~QuickCheckPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: QuickCheck
API changes (from Hackage documentation)
+ Test.Hspec.Meta: --
+ Test.Hspec.Meta: -- <a>Arg</a> is <tt>()</tt>, no argument is required and the
+ Test.Hspec.Meta: -- <a>Example</a> can be run as-is.
+ Test.Hspec.Meta: -- <a>Test.Hspec.Core.Hooks</a> such as <a>around</a>, <a>before</a>,
+ Test.Hspec.Meta: -- <a>mapSubject</a> and similar.
+ Test.Hspec.Meta: -- <tt><a>SpecWith</a> a</tt>, which cannot be executed without turning
+ Test.Hspec.Meta: -- The value of <a>Arg</a> is the difference between <a>Spec</a> (aka
+ Test.Hspec.Meta: -- To supply an argument to examples, use the functions in
+ Test.Hspec.Meta: -- it into <a>Spec</a> first.
+ Test.Hspec.Meta: -- | The argument type that is needed to run this <a>Example</a>. If
Files
- hspec-core/src/Test/Hspec/Core/Example.hs +64/−3
- hspec-core/src/Test/Hspec/Core/Formatters/Pretty.hs +2/−2
- hspec-core/src/Test/Hspec/Core/Formatters/Pretty/Parser.hs +126/−62
- hspec-core/src/Test/Hspec/Core/Formatters/Pretty/Parser/Parser.hs +0/−34
- hspec-core/src/Test/Hspec/Core/Hooks.hs +12/−2
- hspec-core/src/Test/Hspec/Core/QuickCheck/Util.hs +10/−1
- hspec-core/src/Test/Hspec/Core/Spec.hs +15/−0
- hspec-core/src/Test/Hspec/Core/Spec/Monad.hs +31/−2
- hspec-meta.cabal +1/−2
- hspec/src/Test/Hspec.hs +2/−3
hspec-core/src/Test/Hspec/Core/Example.hs view
@@ -40,12 +40,66 @@ import Test.Hspec.Core.QuickCheck.Util (liftHook) import Test.Hspec.Core.Example.Location --- | A type class for examples+-- | A type class for examples, that is to say, test bodies as used in+-- `Test.Hspec.Core.Spec.it` and similar functions. class Example e where+ -- | The argument type that is needed to run this `Example`.+ -- If `Arg` is @()@, no argument is required and the `Example` can be run+ -- as-is.+ --+ -- The value of `Arg` is the difference between `Test.Hspec.Core.Spec.Spec`+ -- (aka @`Test.Hspec.Core.Hspec.SpecWith` ()@), which can be executed, and+ -- @`Test.Hspec.Core.Spec.SpecWith` a@, which cannot be executed without+ -- turning it into `Test.Hspec.Core.Spec.Spec` first.+ --+ -- To supply an argument to examples, use the functions in+ -- "Test.Hspec.Core.Hooks" such as `Test.Hspec.Core.Hooks.around',+ -- `Test.Hspec.Core.Hooks.before', `Test.Hspec.Core.Hooks.mapSubject' and+ -- similar. type Arg e type Arg e = ()- evaluateExample :: e -> Params -> (ActionWith (Arg e) -> IO ()) -> ProgressCallback -> IO Result + -- | Evaluates an example.+ --+ -- `evaluateExample` is expected to execute the test body inside the IO action+ -- passed to the hook. It's often necessary to use an `IORef` to pass data+ -- out like whether the test succeeded to the outer IO action so it can be+ -- returned as a `Result`.+ --+ -- Example:+ --+ -- @+ -- newtype MyAction = MyAction (Int -> IO Bool)+ --+ -- instance Example MyAction where+ -- type Arg MyAction = Int+ --+ -- evaluateExample (MyAction act) _params hook _progress = do+ -- result <- newIORef (Result "" Success)+ -- hook $ \arg -> do+ -- -- e.g. determines if arg is 42+ -- ok <- act arg+ -- let result' = Result "" $ if ok then Success else Failure Nothing NoReason+ -- writeIORef result result'+ -- readIORef result+ -- @+ evaluateExample+ :: e+ -- ^ The example being evaluated+ -> Params+ -- ^ QuickCheck/SmallCheck settings+ -> (ActionWith (Arg e) -> IO ())+ -- ^ Hook: takes an @`ActionWith` (`Arg` e)@, namely, the IO action to run+ -- the test body, obtains @`Arg` e@ from somewhere, then executes the test+ -- body (or possibly decides not to execute it!).+ --+ -- This is used to implement `Test.Hspec.Core.Hooks.around` and similar+ -- hook functionality.+ -> ProgressCallback+ -- ^ Callback for composite tests like QuickCheck to report their progress.+ -> IO Result++-- | QuickCheck and SmallCheck related parameters. data Params = Params { paramsQuickCheckArgs :: QC.Args , paramsSmallCheckDepth :: Maybe Int@@ -57,10 +111,17 @@ , paramsSmallCheckDepth = Nothing } +-- | @(CurrentItem, TotalItems)@ tuple. type Progress = (Int, Int)+-- | Callback used by composite test items that contain many tests to report+-- their progress towards finishing them all.+--+-- This is used, for example, to report how many QuickCheck examples are finished. type ProgressCallback = Progress -> IO () --- | An `IO` action that expects an argument of type @a@+-- | An `IO` action that expects an argument of type @a@.+--+-- This type is what `Example`s are ultimately unlifted into for execution. type ActionWith a = a -> IO () -- | The result of running an example
hspec-core/src/Test/Hspec/Core/Formatters/Pretty.hs view
@@ -56,8 +56,8 @@ go value = case value of Char _ -> False String _ -> True- Rational _ _ -> False Number _ -> False+ Operator _ _ _ -> True Record _ _ -> True Constructor _ xs -> any go xs Tuple xs -> any go xs@@ -97,8 +97,8 @@ render indentation = \ case Char c -> shows c String str -> if unicode then Builder $ ushows str else shows str- Rational n d -> render indentation n <> " % " <> render indentation d Number n -> fromString n+ Operator a name b -> render indentation a <> " " <> fromString name <> " " <> render indentation b Record name fields -> fromString name <> renderFields fields Constructor name values -> intercalate " " (fromString name : map (render indentation) values) Tuple [e@Record{}] -> render indentation e
hspec-core/src/Test/Hspec/Core/Formatters/Pretty/Parser.hs view
@@ -1,107 +1,171 @@+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE ScopedTypeVariables #-} module Test.Hspec.Core.Formatters.Pretty.Parser ( Value(..) , parseValue ) where import Prelude ()-import Test.Hspec.Core.Compat--import Test.Hspec.Core.Formatters.Pretty.Parser.Parser hiding (Parser)-import qualified Test.Hspec.Core.Formatters.Pretty.Parser.Parser as P+import Test.Hspec.Core.Compat hiding (Alternative(..), read, exp) -import Language.Haskell.Lexer hiding (Pos(..))+import Language.Haskell.Lexer hiding (Token, Pos(..))+import qualified Language.Haskell.Lexer as Lexer type Name = String data Value = Char Char | String String- | Rational Value Value | Number String+ | Operator Value Name Value | Record Name [(Name, Value)] | Constructor Name [Value] | Tuple [Value] | List [Value] deriving (Eq, Show) -type Parser = P.Parser (Token, String)+type Token = (TokenType, String) +type TokenType = Lexer.Token++newtype Parser a = Parser {+ runParser :: [Token] -> Maybe (a, [Token])+} deriving Functor++instance Applicative Parser where+ pure a = Parser $ \ input -> Just (a, input)+ (<*>) = ap++instance Monad Parser where+ return = pure+ p1 >>= p2 = Parser $ runParser p1 >=> uncurry (runParser . p2)+ parseValue :: String -> Maybe Value-parseValue input = case runParser value (tokenize input) of+parseValue input = case runParser exp (tokenize input) of Just (v, []) -> Just v _ -> Nothing -value :: Parser Value-value =- char- <|> string- <|> rational- <|> number- <|> record- <|> constructor- <|> tuple- <|> list+tokenize :: String -> [Token]+tokenize = go . map (fmap snd) . rmSpace . lexerPass0+ where+ go :: [Token] -> [Token]+ go tokens = case tokens of+ [] -> []+ (Varsym, "-") : (IntLit, n) : xs -> (IntLit, "-" ++ n) : go xs+ (Varsym, "-") : (FloatLit, n) : xs -> (FloatLit, "-" ++ n) : go xs+ x : xs -> x : go xs -char :: Parser Value-char = Char <$> (token CharLit >>= readA)+exp :: Parser Value+exp = infixexp -string :: Parser Value-string = String <$> (token StringLit >>= readA)+infixexp :: Parser Value+infixexp = aexp accept reject+ where+ accept :: Value -> Parser Value+ accept value = peek >>= \ case+ (Varsym, "%") -> skip >> Operator value "%" <$> exp+ (Consym, name) -> skip >> Operator value name <$> exp+ _ -> return value -rational :: Parser Value-rational = Rational <$> (number <|> tuple) <* require (Varsym, "%") <*> number+ reject :: Parser Value+ reject = next >>= unexpected -number :: Parser Value-number = integer <|> float+aexp :: forall a. (Value -> Parser a) -> Parser a -> Parser a+aexp continue done = peek >>= \ case+ (CharLit, value) -> read Char value+ (StringLit, value) -> read String value+ (IntLit, value) -> number value+ (FloatLit, value) -> number value+ (Conid, name) -> accept $ constructor name+ (Special, "(") -> accept tuple+ (Special, "[") -> accept list+ _ -> done where- integer :: Parser Value- integer = Number <$> token IntLit+ read :: Read v => (v -> Value) -> String -> Parser a+ read c v = skip >> c <$> readValue v >>= continue - float :: Parser Value- float = Number <$> token FloatLit+ number :: String -> Parser a+ number value = skip >> continue (Number value) -record :: Parser Value-record = Record <$> token Conid <* special "{" <*> fields <* special "}"- where- fields :: Parser [(Name, Value)]- fields = field `sepBy1` comma+ accept :: Parser Value -> Parser a+ accept action = skip >> action >>= continue - field :: Parser (Name, Value)- field = (,) <$> token Varid <* equals <*> value+constructor :: String -> Parser Value+constructor name = peek >>= \ case+ (Special, "{") -> skip >> Record name <$> commaSeparated field "}"+ where+ field :: Parser (Name, Value)+ field = (,) <$> require Varid <* equals <*> exp -constructor :: Parser Value-constructor = Constructor <$> token Conid <*> many value+ _ -> Constructor name <$> parameters+ where+ parameters :: Parser [Value]+ parameters = aexp more done + more :: Value -> Parser [Value]+ more v = (:) v <$> parameters++ done :: Parser [Value]+ done = return []+ tuple :: Parser Value-tuple = Tuple <$> (special "(" *> items) <* special ")"+tuple = Tuple <$> commaSeparated exp ")" list :: Parser Value-list = List <$> (special "[" *> items) <* special "]"--items :: Parser [Value]-items = value `sepBy` comma+list = List <$> commaSeparated exp "]" -special :: String -> Parser ()-special s = require (Special, s)+commaSeparated :: forall a. Parser a -> String -> Parser [a]+commaSeparated item close = peek >>= \ case+ (Special, c) | c == close -> skip >> return []+ _ -> items+ where+ items :: Parser [a]+ items = (:) <$> item <*> moreItems -comma :: Parser ()-comma = special ","+ moreItems :: Parser [a]+ moreItems = next >>= \ case+ (Special, c)+ | c == "," -> items+ | c == close -> return []+ t -> unexpected t equals :: Parser ()-equals = require (Reservedop, "=")+equals = requireToken (Reservedop, "=") -token :: Token -> Parser String-token t = snd <$> satisfy (fst >>> (== t))+require :: TokenType -> Parser String+require expected = next >>= \ case+ (token, v) | token == expected -> return v+ token -> unexpected token -require :: (Token, String) -> Parser ()-require t = void $ satisfy (== t)+requireToken :: Token -> Parser ()+requireToken expected = next >>= \ case+ token | token == expected -> return ()+ token -> unexpected token -tokenize :: String -> [(Token, String)]-tokenize = go . map (fmap snd) . rmSpace . lexerPass0- where- go :: [(Token, String)] -> [(Token, String)]- go tokens = case tokens of- [] -> []- (Varsym, "-") : (IntLit, n) : xs -> (IntLit, "-" ++ n) : go xs- (Varsym, "-") : (FloatLit, n) : xs -> (FloatLit, "-" ++ n) : go xs- x : xs -> x : go xs+peek :: Parser Token+peek = Parser $ \ input -> case input of+ t : _ -> Just (t, input)+ [] -> Just ((GotEOF, ""), input)++next :: Parser Token+next = Parser $ \ case+ t : ts -> Just (t, ts)+ ts -> Just ((GotEOF, ""), ts)++skip :: Parser ()+skip = Parser $ \ case+ _ : ts -> Just ((), ts)+ ts -> Just ((), ts)++empty :: Parser a+empty = Parser $ const Nothing++readValue :: Read a => String -> Parser a+readValue = maybe empty pure . readMaybe++unexpected :: Token -> Parser a+unexpected _ = empty++-- unexpected :: HasCallStack => (Token, String) -> Parser a+-- unexpected = error . show
− hspec-core/src/Test/Hspec/Core/Formatters/Pretty/Parser/Parser.hs
@@ -1,34 +0,0 @@-{-# LANGUAGE DeriveFunctor #-}-module Test.Hspec.Core.Formatters.Pretty.Parser.Parser where--import Prelude ()-import Test.Hspec.Core.Compat--newtype Parser token a = Parser { runParser :: [token] -> Maybe (a, [token]) }- deriving Functor--instance Applicative (Parser token) where- pure a = Parser $ \ input -> Just (a, input)- (<*>) = ap--instance Monad (Parser token) where- return = pure- p1 >>= p2 = Parser $ runParser p1 >=> uncurry (runParser . p2)--instance Alternative (Parser token) where- empty = Parser $ const Nothing- p1 <|> p2 = Parser $ \ input -> runParser p1 input <|> runParser p2 input--satisfy :: (token -> Bool) -> Parser token token-satisfy p = Parser $ \ input -> case input of- t : ts | p t -> Just (t, ts)- _ -> Nothing--sepBy :: Alternative m => m a -> m sep -> m [a]-sepBy p sep = sepBy1 p sep <|> pure []--sepBy1 :: Alternative m => m a -> m sep -> m [a]-sepBy1 p sep = (:) <$> p <*> many (sep *> p)--readA :: (Alternative m, Read a) => String -> m a-readA = maybe empty pure . readMaybe
hspec-core/src/Test/Hspec/Core/Hooks.hs view
@@ -96,8 +96,18 @@ after_ :: IO () -> SpecWith a -> SpecWith a after_ action = after $ \_ -> action --- | Run a custom action before and/or after every spec item.-around :: (ActionWith a -> IO ()) -> SpecWith a -> Spec+-- | Run a custom action before and/or after every spec item, supplying it with+-- an argument obtained via IO.+--+-- This is useful for tasks like creating a file handle or similar resource+-- before a test and destroying it after the test.+around+ :: (ActionWith a -> IO ())+ -- ^ Function provided with an action to run the spec item as argument. It+ -- should return the action to actually execute the item.+ -> SpecWith a+ -- ^ Spec to modify+ -> Spec around action = aroundWith $ \e () -> action e -- | Run a custom action after the last spec item.
hspec-core/src/Test/Hspec/Core/QuickCheck/Util.hs view
@@ -37,7 +37,16 @@ import Test.Hspec.Core.Util -liftHook :: r -> ((a -> IO ()) -> IO ()) -> (a -> IO r) -> IO r+liftHook+ :: r+ -- ^ Default result, taken if the hook decides to not run the test+ -> ((a -> IO ()) -> IO ())+ -- ^ Hook: given an action to actually run the test, potentially does+ -- some IO actions before the test, runs it while supplying an argument (or+ -- skips it!), then may execute some more actions after running it.+ -> (a -> IO r)+ -- ^ The actual test being run, producing a result+ -> IO r liftHook def hook inner = do ref <- newIORef def hook $ inner >=> writeIORef ref
hspec-core/src/Test/Hspec/Core/Spec.hs view
@@ -19,6 +19,10 @@ , xdescribe , xcontext +-- * Focused spec items #focus#+-- |+-- During a test run, when a spec contains /focused/ spec items, all other spec+-- items are ignored. , focus , fit , fspecify@@ -125,6 +129,16 @@ -- > describe "absolute" $ do -- > it "returns a positive number when given a negative number" $ -- > absolute (-1) == 1+--+-- @`Example` a@ optionally accepts an argument @`Arg` a@, which is then given+-- to the test body. This is useful for provisioning resources for a test which+-- are created and cleaned up outside the test itself. See `Arg` for details.+--+-- Note that this function is often on the scene of nasty type errors due to GHC failing+-- to infer the type of @do@ notation in the test body.+-- It can be helpful to use+-- [TypeApplications](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/type_applications.html)+-- to explicitly specify the intended `Example` type. it :: (HasCallStack, Example a) => String -> a -> SpecWith (Arg a) it label action = fromSpecList [specItem label action] @@ -149,6 +163,7 @@ focus :: SpecWith a -> SpecWith a focus = mapSpecForest focusForest +-- | Marks an entire spec forest as focused if nothing in it is already focused. focusForest :: [SpecTree a] -> [SpecTree a] focusForest xs | any (any itemIsFocused) xs = xs
hspec-core/src/Test/Hspec/Core/Spec/Monad.hs view
@@ -32,8 +32,24 @@ import Test.Hspec.Core.Config.Definition (Config) +-- | A `SpecWith` that can be evaluated directly by the+-- `Test.Hspec.Core.Runner.hspec` function as it does not require any+-- parameters. type Spec = SpecWith () +-- | A @'SpecWith' a@ represents a test or group of tests that require an @a@+-- value to run.+--+-- In the common case, a 'Spec' is a @'SpecWith' ()@ which requires @()@ and+-- can thus be executed with `Test.Hspec.Core.Runner.hspec'.+--+-- To supply an argument to `SpecWith` tests to turn them into `Spec`, use+-- functions from "Test.Hspec.Core.Hooks" such as+-- `Test.Hspec.Core.Hooks.around`, `Test.Hspec.Core.Hooks.before',+-- `Test.Hspec.Core.Hooks.mapSubject' and similar.+--+-- Values of this type are created by `Test.Hspec.Core.Spec.it`,+-- `Test.Hspec.Core.Spec.describe` and similar. type SpecWith a = SpecM a () -- |@@ -41,7 +57,13 @@ modifyConfig :: (Config -> Config) -> SpecWith a modifyConfig f = SpecM $ tell (Endo f, mempty) --- | A writer monad for `SpecTree` forests+-- | A writer monad for `SpecTree` forests.+--+-- This is used by `Test.Hspec.Core.Spec.describe` and is used+-- to construct the forest of spec items.+--+-- It allows for dynamically generated spec trees, for example, by using data+-- obtained by performing IO actions with 'runIO'. newtype SpecM a r = SpecM { unSpecM :: WriterT (Endo Config, [SpecTree a]) (ReaderT Env IO) r } deriving (Functor, Applicative, Monad) @@ -60,7 +82,7 @@ -- | Run an IO action while constructing the spec tree. -- -- `SpecM` is a monad to construct a spec tree, without executing any spec--- items. @runIO@ allows you to run IO actions during this construction phase.+-- items itself. @runIO@ allows you to run IO actions during this construction phase. -- The IO action is always run when the spec tree is constructed (e.g. even -- when @--dry-run@ is specified). -- If you do not need the result of the IO action to construct the spec tree,@@ -79,12 +101,19 @@ mapSpecItem_ :: (Item a -> Item b) -> SpecWith a -> SpecWith b mapSpecItem_ = mapSpecForest . bimapForest id +-- | Modifies the `Params` on the spec items to be generated by `SpecWith`. modifyParams :: (Params -> Params) -> SpecWith a -> SpecWith a modifyParams f = mapSpecItem_ $ \item -> item {itemExample = \p -> (itemExample item) (f p)} newtype Env = Env {+ -- | The path of _parent_ `Hspec.Core.Spec.describe` labels from innermost to+ -- outermost. envSpecDescriptionPath :: [String] } +-- | Applies a function to modify the `Env` of items being written by a child+-- spec writer.+--+-- This is used to implement `Hspec.Core.Spec.describe`. withEnv :: (Env -> Env) -> SpecM a r -> SpecM a r withEnv f = SpecM . WriterT . local f . runWriterT . unSpecM
hspec-meta.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: hspec-meta-version: 2.11.13+version: 2.11.14 synopsis: A version of Hspec which is used to test Hspec itself description: A stable version of Hspec which is used to test the in-development version of Hspec.@@ -66,7 +66,6 @@ Test.Hspec.Core.Formatters.Internal Test.Hspec.Core.Formatters.Pretty Test.Hspec.Core.Formatters.Pretty.Parser- Test.Hspec.Core.Formatters.Pretty.Parser.Parser Test.Hspec.Core.Formatters.Pretty.Unicode Test.Hspec.Core.Formatters.V1 Test.Hspec.Core.Formatters.V1.Free
hspec/src/Test/Hspec.hs view
@@ -10,8 +10,7 @@ -- * Types Spec , SpecWith-, Arg-, Example+, Example(Arg) -- * Setting expectations , module Test.Hspec.Expectations@@ -40,7 +39,7 @@ , xdescribe , xcontext --- * Focused spec items+-- * Focused spec items #focus# -- | -- During a test run, when a spec contains /focused/ spec items, all other spec -- items are ignored.