packages feed

hspec 1.5.4 → 1.6.0

raw patch · 32 files changed

+841/−446 lines, 32 filesdep ~basedep ~transformersPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: base, transformers

API changes (from Hackage documentation)

- Test.Hspec.Runner: ColorAlway :: ColorMode
- Test.Hspec.Runner: configReRun :: Config -> Bool
- Test.Hspec.Runner: configVerbose :: Config -> Bool
+ Test.Hspec: parallel :: Spec -> Spec
+ Test.Hspec.Formatters: class IsFormatter a
+ Test.Hspec.Formatters: instance IsFormatter (IO Formatter)
+ Test.Hspec.Formatters: instance IsFormatter Formatter
+ Test.Hspec.Formatters: toFormatter :: IsFormatter a => a -> IO Formatter
+ Test.Hspec.Runner: ColorAlways :: ColorMode
+ Test.Hspec.Runner: hspecResult :: Spec -> IO Summary
+ Test.Hspec.Runner: hspecWithFormatter :: IsFormatter a => a -> Spec -> IO ()
- Test.Hspec: it :: Example v => String -> v -> Spec
+ Test.Hspec: it :: Example a => String -> a -> Spec
- Test.Hspec.Core: SpecItem :: String -> (Params -> IO Result) -> SpecTree
+ Test.Hspec.Core: SpecItem :: Bool -> String -> (Params -> IO Result) -> SpecTree
- Test.Hspec.Monadic: it :: Example v => String -> v -> Spec
+ Test.Hspec.Monadic: it :: Example a => String -> a -> Spec
- Test.Hspec.Runner: Config :: Bool -> Bool -> Bool -> Bool -> Bool -> Maybe (Path -> Bool) -> Args -> ColorMode -> Formatter -> Bool -> Handle -> Config
+ Test.Hspec.Runner: Config :: Bool -> Bool -> Bool -> Maybe (Path -> Bool) -> Args -> ColorMode -> Formatter -> Bool -> Handle -> Config

Files

LICENSE view
@@ -1,10 +1,21 @@--Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:-+Copyright (c) 2011-2013 Simon Hengel <sol@typeful.net>+Copyright (c) 2011-2012 Trystan Spangler <trystan.s@comcast.net>+Copyright (c) 2011-2011 Greg Weber <greg@gregweber.info> -Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.-Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.-The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.+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: -THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+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.
+ hspec-discover/integration-test/with-formatter-empty/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover -optF --formatter=Test.Hspec.Formatters.progress #-}
+ hspec-discover/integration-test/with-formatter/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover -optF --formatter=Test.Hspec.Formatters.progress #-}
+ hspec-discover/integration-test/with-io-formatter/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover -optF --formatter=Formatter.count #-}
+ hspec-discover/src/Config.hs view
@@ -0,0 +1,33 @@+module Config (+  Config (..)+, defaultConfig+, parseConfig+, usage+) where++import           System.Console.GetOpt++data Config = Config {+  configNested :: Bool+, configFormatter :: Maybe String+} deriving (Eq, Show)++defaultConfig :: Config+defaultConfig = Config False Nothing++options :: [OptDescr (Config -> Config)]+options = [+    Option [] ["nested"]    (NoArg  $ \c -> c   {configNested = True}) ""+  , Option [] ["formatter"] (ReqArg (\s c -> c {configFormatter = Just s}) "FORMATTER") ""+  ]++usage :: String -> String+usage prog = "\nUsage: " ++ prog ++ " SRC CUR DST [--nested] [--formatter=FORMATTER]\n"++parseConfig :: String -> [String] -> Either String Config+parseConfig prog args = case getOpt Permute options args of+    (opts, [], []) -> Right (foldl (flip id) defaultConfig opts)+    (_, _, err:_)  -> formatError err+    (_, arg:_, _)  -> formatError ("unexpected argument `" ++ arg ++ "'\n")+  where+    formatError err = Left (prog ++ ": " ++ err ++ usage prog)
hspec-discover/src/Run.hs view
@@ -14,39 +14,54 @@ import           System.Directory import           System.FilePath hiding (combine) +import           Config+ instance IsString ShowS where   fromString = showString  run :: [String] -> IO ()-run args_ = case args_ of-  src : _ : dst : args -> do-    nested <- case args of-      []           -> return False-      ["--nested"] -> return True-      _            -> exit-    specs <- findSpecs src-    writeFile dst (mkSpecModule src nested specs)-  _ -> exit-  where-    exit = do-      name <- getProgName-      hPutStrLn stderr ("usage: " ++ name ++ " SRC CUR DST [--nested]")+run args_ = do+  name <- getProgName+  case args_ of+    src : _ : dst : args -> case parseConfig name args of+      Left err -> do+        hPutStrLn stderr err+        exitFailure+      Right c -> do+        specs <- findSpecs src+        writeFile dst (mkSpecModule src c specs)+    _ -> do+      hPutStrLn stderr (usage name)       exitFailure -mkSpecModule :: FilePath -> Bool -> [SpecNode] -> String-mkSpecModule src nested nodes =+mkSpecModule :: FilePath -> Config -> [SpecNode] -> String+mkSpecModule src c nodes =   ( "{-# LINE 1 " . shows src . " #-}"   . showString "module Main where\n"-  . showString "import Test.Hspec\n"   . importList nodes-  . showString "main :: IO ()\n"-  . showString "main = hspec $ "+  . maybe driver (driverWithFormatter (null nodes)) (configFormatter c)   . format nodes   ) "\n"   where     format-      | nested    = formatSpecsNested+      | configNested c = formatSpecsNested       | otherwise = formatSpecs++    driver =+        showString "import Test.Hspec\n"+      . showString "main :: IO ()\n"+      . showString "main = hspec $ "++driverWithFormatter :: Bool -> String -> ShowS+driverWithFormatter isEmpty f =+    (if isEmpty then id else "import Test.Hspec\n")+  . showString "import Test.Hspec.Runner\n"+  . showString "import qualified " . showString (moduleName f) . showString "\n"+  . showString "main :: IO ()\n"+  . showString "main = hspecWithFormatter " . showString f . showString " $ "++moduleName :: String -> String+moduleName = reverse . dropWhile (== '.') . dropWhile (/= '.') . reverse  -- | Generate imports for a list of specs. importList :: [SpecNode] -> ShowS
+ hspec-discover/test/ConfigSpec.hs view
@@ -0,0 +1,37 @@+module ConfigSpec (main, spec) where++import           Test.Hspec.Meta++import           Config++main :: IO ()+main = hspec spec++spec :: Spec+spec = do+  describe "parseConfig" $ do+    let parse = parseConfig "hspec-discover"++    it "recognizes --nested" $ do+      parse ["--nested"] `shouldBe` Right (defaultConfig {configNested = True})++    it "recognizes --formatter" $ do+      parse ["--formatter", "someFormatter"] `shouldBe` Right (defaultConfig {configFormatter = Just "someFormatter"})++    it "returns error message on unrecognized option" $ do+      parse ["--foo"] `shouldBe` (Left . unlines) [+          "hspec-discover: unrecognized option `--foo'"+        , ""+        , "Usage: hspec-discover SRC CUR DST [--nested] [--formatter=FORMATTER]"+        ]++    it "returns error message on unexpected argument" $ do+      parse ["foo"]   `shouldBe` (Left . unlines) [+          "hspec-discover: unexpected argument `foo'"+        , ""+        , "Usage: hspec-discover SRC CUR DST [--nested] [--formatter=FORMATTER]"+        ]++    context "when option is given multiple times" $ do+      it "the last occurrence takes precedence" $ do+        parse ["--formatter", "foo", "--formatter", "bar"] `shouldBe` Right (defaultConfig {configFormatter = Just "bar"})
hspec-discover/test/RunSpec.hs view
@@ -1,8 +1,9 @@-{-# LANGUAGE OverloadedStrings #-} module RunSpec (main, spec) where  import           Test.Hspec.Meta +import           Data.List (intercalate)+ import           Run  main :: IO ()@@ -41,6 +42,20 @@     context "when there are no specs" $ do       it "returns an empty list" $ do         findSpecs "hspec-discover/test-data/empty-dir/Spec.hs" `shouldReturn` []++  describe "driverWithFormatter" $ do+    it "generates a test driver that uses a custom formatter" $ do+      driverWithFormatter False "Some.Module.formatter" "" `shouldBe` intercalate "\n" [+          "import Test.Hspec"+        , "import Test.Hspec.Runner"+        , "import qualified Some.Module"+        , "main :: IO ()"+        , "main = hspecWithFormatter Some.Module.formatter $ "+        ]++  describe "moduleName" $ do+    it "returns the module name of an fully qualified identifier" $ do+      moduleName "Some.Module.someId" `shouldBe` "Some.Module"    describe "formatSpec" $ do     it "generates code for a spec" $
hspec.cabal view
@@ -1,6 +1,6 @@ name:             hspec-version:          1.5.4-license:          BSD3+version:          1.6.0+license:          MIT license-file:     LICENSE copyright:        (c) 2011-2013 Simon Hengel,                   (c) 2011-2012 Trystan Spangler,@@ -11,7 +11,7 @@ category:         Testing stability:        experimental bug-reports:      https://github.com/hspec/hspec/issues-homepage:         http://hspec.github.com/+homepage:         http://hspec.github.io/ synopsis:         Behavior-Driven Development for Haskell description:      Behavior-Driven Development for Haskell                   .@@ -20,7 +20,7 @@                   tests. Compared to other options, it provides a much nicer                   syntax that makes tests very easy to read.                   .-                  The Hspec Manual is at <http://hspec.github.com/>.+                  The Hspec Manual is at <http://hspec.github.io/>.  -- find hspec-discover/test-data/ -type f extra-source-files:@@ -76,7 +76,9 @@       Test.Hspec.Compat       Test.Hspec.Core.Type       Test.Hspec.Config+      Test.Hspec.Options       Test.Hspec.FailureReport+      Test.Hspec.Runner.Eval       Test.Hspec.Formatters.Internal       Test.Hspec.Timer @@ -89,13 +91,14 @@       Spec.hs   other-modules:       Mock-      SpecHelper+      Helper       Test.HspecSpec       Test.Hspec.CompatSpec       Test.Hspec.Core.TypeSpec       Test.Hspec.FailureReportSpec       Test.Hspec.FormattersSpec       Test.Hspec.HUnitSpec+      Test.Hspec.OptionsSpec       Test.Hspec.QuickCheckSpec       Test.Hspec.RunnerSpec       Test.Hspec.TimerSpec@@ -157,6 +160,7 @@       Main.hs   other-modules:       Run+      Config   build-depends:       base    == 4.*     , filepath@@ -174,6 +178,7 @@       Spec.hs   other-modules:       RunSpec+      ConfigSpec   build-depends:       base    == 4.*     , filepath@@ -203,6 +208,49 @@       -Wall -Werror   hs-source-dirs:       hspec-discover/integration-test/empty+  main-is:+      Spec.hs+  build-depends:+      base    == 4.*+    , hspec++test-suite hspec-discover-integration-test-with-formatter+  buildable: False+  type:+      exitcode-stdio-1.0+  ghc-options:+      -Wall -Werror+  hs-source-dirs:+      hspec-discover/integration-test/with-formatter+  main-is:+      Spec.hs+  build-depends:+      base    == 4.*+    , hspec++test-suite hspec-discover-integration-test-with-io-formatter+  buildable: False+  type:+      exitcode-stdio-1.0+  ghc-options:+      -Wall -Werror+  hs-source-dirs:+      hspec-discover/integration-test/with-io-formatter+  main-is:+      Spec.hs+  build-depends:+      base    == 4.*+    , hspec+    , transformers++test-suite hspec-discover-integration-test-with-formatter-empty+  buildable: False+  type:+      exitcode-stdio-1.0+  ghc-options:+      -Wall -Werror+  hs-source-dirs:+      hspec-discover/integration-test/with-formatter-empty   main-is:       Spec.hs   build-depends:
src/Test/Hspec.hs view
@@ -32,6 +32,7 @@ , example , pending , pendingWith+, parallel  -- * Running a spec , hspec@@ -124,7 +125,7 @@ -- > describe "absolute" $ do -- >   it "returns a positive number when given a negative number" $ -- >     absolute (-1) == 1-it :: Example v => String -> v -> Spec+it :: Example a => String -> a -> Spec it label action = fromSpecList [Core.it label action]  -- | This is a type restricted version of `id`.  It can be used to get better@@ -141,3 +142,12 @@ -- >   putStrLn example :: Expectation -> Expectation example = id++-- | Run examples of given spec in parallel.+parallel :: Spec -> Spec+parallel = fromSpecList . map go . runSpecM+  where+    go :: SpecTree -> SpecTree+    go spec = case spec of+      SpecItem _ r e -> SpecItem True r e+      SpecGroup d es -> SpecGroup d (map go es)
src/Test/Hspec/Config.hs view
@@ -1,21 +1,15 @@ module Test.Hspec.Config (   Config (..)-, ColorMode (..) , defaultConfig , getConfig , configAddFilter , configSetSeed---- exported to silence warnings-, Arg (..) ) where -import           Control.Monad (unless) import           Control.Applicative+import           Data.List import           System.IO import           System.Exit-import           System.Environment-import           System.Console.GetOpt import qualified Test.QuickCheck as QC import           Test.Hspec.Formatters @@ -24,11 +18,12 @@ -- for Monad (Either e) when base < 4.3 import           Control.Monad.Trans.Error () +import           Test.Hspec.Options+import           Test.Hspec.FailureReport+ data Config = Config {-  configVerbose         :: Bool-, configDryRun          :: Bool+  configDryRun          :: Bool , configPrintCpuTime    :: Bool-, configReRun           :: Bool , configFastFail        :: Bool  -- |@@ -42,140 +37,69 @@ , configHandle          :: Handle } -{-# DEPRECATED configVerbose "this has no effect anymore and will be removed with a future release" #-} -- deprecated since 1.5.0--data ColorMode = ColorAuto | ColorNever | ColorAlway- defaultConfig :: Config-defaultConfig = Config False False False False False Nothing QC.stdArgs ColorAuto specdoc False stdout--formatters :: [(String, Formatter)]-formatters = [-    ("specdoc", specdoc)-  , ("progress", progress)-  , ("failed-examples", failed_examples)-  , ("silent", silent)-  ]--formatHelp :: String-formatHelp = unlines (addLineBreaks "use a custom formatter; this can be one of:" ++ map (("   " ++) . fst) formatters)--type Result = Either NoConfig Config--data NoConfig = Help | InvalidArgument String String+defaultConfig = Config False False False Nothing QC.stdArgs ColorAuto specdoc False stdout  -- | Add a filter predicate to config.  If there is already a filter predicate, -- then combine them with `||`. configAddFilter :: (Path -> Bool) -> Config -> Config-configAddFilter p1 c = c {configFilterPredicate = Just p}-  where-    -- if there is already a predicate, we combine them with ||-    p  = maybe p1 (\p0 path -> p0 path || p1 path) mp-    mp = configFilterPredicate c+configAddFilter p1 c = c {+    configFilterPredicate = Just p1 `filterOr` configFilterPredicate c+  } -setMaxSuccess :: Int -> Config -> Config-setMaxSuccess n c = c {configQuickCheckArgs = (configQuickCheckArgs c) {QC.maxSuccess = n}}+filterOr :: Maybe (Path -> Bool) -> Maybe (Path -> Bool) -> Maybe (Path -> Bool)+filterOr p1_ p2_ = case (p1_, p2_) of+  (Just p1, Just p2) -> Just $ \path -> p1 path || p2 path+  _ -> p1_ <|> p2_  configSetSeed :: Integer -> Config -> Config configSetSeed n c = c {configQuickCheckArgs = (configQuickCheckArgs c) {QC.replay = Just (stdGenFromInteger n, 0)}} --data Arg a = Arg {-  argumentName   :: String-, argumentParser :: String -> Maybe a-, argumentSetter :: a -> Config -> Config-}--mkOption :: [Char] -> [Char] -> Arg a -> String -> OptDescr (Result -> Result)-mkOption shortcut name (Arg argName parser setter) help = Option shortcut [name] (ReqArg arg argName) help-  where-    arg :: String -> Result -> Result-    arg input x = x >>= \c -> case parser input of-      Just n -> Right (setter n c)-      Nothing -> Left (InvalidArgument name input)--addLineBreaks :: String -> [String]-addLineBreaks = lineBreaksAt 44--options :: [OptDescr (Result -> Result)]-options = [-    Option   []  ["help"]             (NoArg (const $ Left Help))         (h "display this help and exit")-  , mkOption "m"  "match"             (Arg "PATTERN" return setFilter)    (h "only run examples that match given PATTERN")-  , Option   []  ["color"]            (OptArg setColor "WHEN")            (h "colorize the output; WHEN defaults to `always' or can be `never' or `auto'")-  , mkOption "f"  "format"            (Arg "FORMATTER" readFormatter setFormatter) formatHelp-  , mkOption "a"  "qc-max-success"    (Arg "N" readMaybe setMaxSuccess)   (h "maximum number of successful tests before a QuickCheck property succeeds")-  , mkOption []   "seed"              (Arg "N" readMaybe configSetSeed)   (h "used seed for QuickCheck properties")-  , Option   []  ["print-cpu-time"]   (NoArg setPrintCpuTime)             (h "include used CPU time in summary")-  , Option   []  ["dry-run"]          (NoArg setDryRun)                   (h "pretend that everything passed; don't verify anything")-  , Option   []  ["fail-fast"]        (NoArg setFastFail)                 (h "abort on first failure")-  ]+mkConfig :: Maybe FailureReport -> Options -> Config+mkConfig mFailureReport opts = Config {+    configDryRun          = optionsDryRun opts+  , configPrintCpuTime    = optionsPrintCpuTime opts+  , configFastFail        = optionsFastFail opts+  , configFilterPredicate = matchFilter `filterOr` rerunFilter+  , configQuickCheckArgs  = qcArgs+  , configColorMode       = optionsColorMode opts+  , configFormatter       = optionsFormatter opts+  , configHtmlOutput      = optionsHtmlOutput opts+  , configHandle          = stdout+  }   where-    h = unlines . addLineBreaks--    setFilter :: String -> Config -> Config-    setFilter = configAddFilter . filterPredicate--    readFormatter :: String -> Maybe Formatter-    readFormatter = (`lookup` formatters)--    setFormatter :: Formatter -> Config -> Config-    setFormatter f c = c {configFormatter = f}--    setPrintCpuTime x = x >>= \c -> return c {configPrintCpuTime = True}-    setDryRun       x = x >>= \c -> return c {configDryRun       = True}-    setFastFail     x = x >>= \c -> return c {configFastFail     = True}--    setColor mValue x = x >>= \c -> parseColor mValue >>= \v -> return c {configColorMode = v}+    qcArgs = maybe id setSeed mSeed args       where-        parseColor s = case s of-          Nothing       -> return ColorAlway-          Just "auto"   -> return ColorAuto-          Just "never"  -> return ColorNever-          Just "always" -> return ColorAlway-          Just v        -> Left (InvalidArgument "color" v)--undocumentedOptions :: [OptDescr (Result -> Result)]-undocumentedOptions = [-    Option "r" ["re-run"]                  (NoArg  setReRun)                  "only re-run examples that previously failed"--    -- for compatibility with test-framework-  , mkOption [] "maximum-generated-tests" (Arg "NUMBER" readMaybe setMaxSuccess) "how many automated tests something like QuickCheck should try, by default"+        args = maybe id setMaxSuccess mMaxSuccess QC.stdArgs -    -- undocumented for now, as we probably want to change this to produce a-    -- standalone HTML report in the future-  , Option []  ["html"]                    (NoArg setHtml)                    "produce HTML output"+    mSeed = optionsSeed opts <|> (failureReportSeed <$> mFailureReport)+    mMaxSuccess = optionsMaxSuccess opts <|> (failureReportMaxSuccess <$> mFailureReport) -    -- now a noop-  , Option "v" ["verbose"]                 (NoArg id)                         "do not suppress output to stdout when evaluating examples"-  ]-  where-    setReRun :: Result -> Result-    setReRun x = x >>= \c -> return c {configReRun = True}+    setMaxSuccess :: Int -> QC.Args -> QC.Args+    setMaxSuccess n args = args {QC.maxSuccess = n} -    setHtml :: Result -> Result-    setHtml x = x >>= \c -> return c {configHtmlOutput = True}+    setSeed :: Integer -> QC.Args -> QC.Args+    setSeed n args = args {QC.replay = Just (stdGenFromInteger n, 0)} -getConfig :: IO Config-getConfig = do-  (opts, args, errors) <- getOpt Permute (options ++ undocumentedOptions) <$> getArgs+    matchFilter = case optionsMatch opts of+      [] -> Nothing+      xs -> Just $ foldl1' (\p0 p1 path -> p0 path || p1 path) (map filterPredicate xs) -  unless (null errors)-    (tryHelp $ head errors)+    rerunFilter = flip elem . failureReportPaths <$> mFailureReport -  unless (null args)-    (tryHelp $ "unexpected argument `" ++ head args ++ "'\n")+getConfig :: Options -> String -> [String] -> IO Config+getConfig opts_ prog args = do+  case parseOptions opts_ prog args of+    Left (err, msg) -> exitWithMessage err msg+    Right opts -> do+      r <- if optionsRerun opts then readFailureReport else return Nothing+      return (mkConfig r opts) -  case foldl (flip id) (Right defaultConfig) opts of-    Left Help -> do-      name <- getProgName-      putStr $ usageInfo ("Usage: " ++ name ++ " [OPTION]...\n\nOPTIONS") options-      exitSuccess-    Left (InvalidArgument flag value) -> do-      tryHelp $ "invalid argument `" ++ value ++ "' for `--" ++ flag ++ "'\n"-    Right config -> do-      return config+exitWithMessage :: ExitCode -> String -> IO a+exitWithMessage err msg = do+  hPutStr h msg+  exitWith err   where-    tryHelp message = do-      name <- getProgName-      hPutStr stderr $ name ++ ": " ++ message ++ "Try `" ++ name ++ " --help' for more information.\n"-      exitFailure+    h = case err of+      ExitSuccess -> stdout+      _           -> stderr
src/Test/Hspec/Core/Type.hs view
@@ -58,7 +58,7 @@  forceResult :: Result -> Result forceResult r = case r of-  Success   -> r `seq` r+  Success   -> r   Pending m -> r `seq` m `deepseq` r   Fail    m -> r `seq` m `deepseq` r @@ -74,7 +74,7 @@ -- | Internal representation of a spec. data SpecTree =     SpecGroup String [SpecTree]-  | SpecItem  String (Params -> IO Result)+  | SpecItem Bool String (Params -> IO Result)  -- | The @describe@ function combines a list of specs into a larger spec. describe :: String -> [SpecTree] -> SpecTree@@ -86,7 +86,7 @@  -- | Create a spec item. it :: Example a => String -> a -> SpecTree-it s e = SpecItem msg (`evaluateExample` e)+it s e = SpecItem False msg (`evaluateExample` e)   where     msg       | null s = "(unspecified behavior)"@@ -106,7 +106,7 @@     ]  instance Example Result where-  evaluateExample _ r = return r+  evaluateExample _ = return  instance Example QC.Property where   evaluateExample c p = do
src/Test/Hspec/FailureReport.hs view
@@ -1,17 +1,20 @@ module Test.Hspec.FailureReport (-  writeFailureReport+  FailureReport (..)+, writeFailureReport , readFailureReport ) where  import           System.IO import           System.SetEnv-import qualified Test.QuickCheck as QC-import           Test.Hspec.Config import           Test.Hspec.Util (Path, safeTry, readMaybe, getEnv) -type Seed = Integer+data FailureReport = FailureReport {+  failureReportSeed :: Integer+, failureReportMaxSuccess :: Int+, failureReportPaths :: [Path]+} deriving (Eq, Show, Read) -writeFailureReport :: (Seed, [Path]) -> IO ()+writeFailureReport :: FailureReport -> IO () writeFailureReport x = do   -- on Windows this can throw an exception when the input is too large, hence   -- we use `safeTry` here@@ -20,19 +23,11 @@     onError err = do       hPutStrLn stderr ("WARNING: Could not write environment variable HSPEC_FAILURES (" ++ show err ++ ")") -readFailureReport :: Config -> IO Config-readFailureReport c = do+readFailureReport :: IO (Maybe FailureReport)+readFailureReport = do   mx <- getEnv "HSPEC_FAILURES"   case mx >>= readMaybe of     Nothing -> do-      hPutStrLn stderr "WARNING: Could not read environment variable HSPEC_FAILURES; `--re-run' is ignored!"-      return c-    Just (seed, xs) -> do-      (return . setSeed seed . configAddFilter (`elem` xs)) c--setSeed :: Seed -> Config -> Config-setSeed seed c-  | hasSeed = c-  | otherwise = configSetSeed seed c-  where-    hasSeed = maybe False (const True) (QC.replay $ configQuickCheckArgs c)+      hPutStrLn stderr "WARNING: Could not read environment variable HSPEC_FAILURES; `--rerun' is ignored!"+      return Nothing+    x -> return x
src/Test/Hspec/Formatters.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE FlexibleInstances #-} -- | -- Stability: experimental --@@ -46,6 +47,16 @@  -- ** Helpers , formatException++-- * Using custom formatters with @hspec-discover@+-- |+-- Anything that is an instance of `IsFormatter` can be used by+-- @hspec-discover@ as the default formatter for a spec.  If you have a+-- formatter @myFormatter@ in the module @Custom.Formatters@ you can use it+-- by passing an additional argument to @hspec-discover@.+--+-- >{-# OPTIONS_GHC -F -pgmF hspec-discover -optF --formatter=Custom.Formatters.myFormatter #-}+, IsFormatter (..) ) where  import           Data.Maybe@@ -87,6 +98,14 @@   , withFailColor   ) +class IsFormatter a where+  toFormatter :: a -> IO Formatter++instance IsFormatter (IO Formatter) where+  toFormatter = id++instance IsFormatter Formatter where+  toFormatter = return  silent :: Formatter silent = Formatter {
src/Test/Hspec/Formatters/Internal.hs view
@@ -27,7 +27,6 @@  -- * Functions for internal use , runFormatM-, liftIO , increaseSuccessCount , increasePendingCount , increaseFailCount@@ -42,7 +41,7 @@ import           Control.Exception (SomeException, AsyncException(..), bracket_, try, throwIO) import           System.Console.ANSI import           Control.Monad.Trans.State hiding (gets, modify)-import qualified Control.Monad.IO.Class as IOClass+import           Control.Monad.IO.Class import qualified System.CPUTime as CPUTime import           Data.Time.Clock.POSIX (POSIXTime, getPOSIXTime) @@ -53,20 +52,12 @@ -- | A lifted version of `Control.Monad.Trans.State.gets` gets :: (FormatterState -> a) -> FormatM a gets f = FormatM $ do-  f <$> (get >>= IOClass.liftIO . readIORef)+  f <$> (get >>= liftIO . readIORef)  -- | A lifted version of `Control.Monad.Trans.State.modify` modify :: (FormatterState -> FormatterState) -> FormatM () modify f = FormatM $ do-  get >>= IOClass.liftIO . (`modifyIORef'` f)---- | A lifted version of `IOClass.liftIO`------ This is meant for internal use only, and not part of the public API.  This--- is also the reason why we do not make FormatM an instance MonadIO, so we--- have narrow control over the visibilty of this function.-liftIO :: IO a -> FormatM a-liftIO action = FormatM (IOClass.liftIO action)+  get >>= liftIO . (`modifyIORef'` f)  data FormatterState = FormatterState {   stateHandle     :: Handle@@ -93,7 +84,7 @@ -- NOTE: We use an IORef here, so that the state persists when UserInterrupt is -- thrown. newtype FormatM a = FormatM (StateT (IORef FormatterState) IO a)-  deriving (Functor, Applicative, Monad)+  deriving (Functor, Applicative, Monad, MonadIO)  runFormatM :: Bool -> Bool -> Bool -> Integer -> Handle -> FormatM a -> IO a runFormatM useColor produceHTML_ printCpuTime seed handle (FormatM action) = do@@ -131,7 +122,7 @@ getTotalCount = gets totalCount  -- | Append to the list of accumulated failure messages.-addFailMessage :: Path -> (Either SomeException String) -> FormatM ()+addFailMessage :: Path -> Either SomeException String -> FormatM () addFailMessage p m = modify $ \s -> s {failMessages = FailureRecord p m : failMessages s}  -- | Get the list of accumulated failure messages.
src/Test/Hspec/HUnit.hs view
@@ -4,7 +4,7 @@   fromHUnitTest ) where -import           Data.List (intersperse)+import           Data.List (intercalate) import qualified Test.HUnit as HU import           Test.HUnit (Test (..)) @@ -20,7 +20,7 @@     return r     where       details :: String -> String-      details = concat . intersperse "\n" . tail . init . lines+      details = intercalate "\n" . tail . init . lines  -- | -- Convert a HUnit test suite to a spec.  This can be used to run existing
+ src/Test/Hspec/Options.hs view
@@ -0,0 +1,136 @@+module Test.Hspec.Options (+  Options (..)+, ColorMode (..)+, defaultOptions+, parseOptions++-- exported to silence warnings+, Arg (..)+) where++import           Data.List+import           System.Exit+import           System.Console.GetOpt+import           Test.Hspec.Formatters++import           Test.Hspec.Util++-- for Monad (Either e) when base < 4.3+import           Control.Monad.Trans.Error ()++data Options = Options {+  optionsDryRun       :: Bool+, optionsPrintCpuTime :: Bool+, optionsRerun        :: Bool+, optionsFastFail     :: Bool+, optionsMatch        :: [String]+, optionsMaxSuccess   :: Maybe Int+, optionsSeed         :: Maybe Integer+, optionsColorMode    :: ColorMode+, optionsFormatter    :: Formatter+, optionsHtmlOutput   :: Bool+}++addMatch :: String -> Options -> Options+addMatch s c = c {optionsMatch = s : optionsMatch c}++setMaxSuccess :: Int -> Options -> Options+setMaxSuccess n c = c {optionsMaxSuccess = Just n}++setSeed :: Integer -> Options -> Options+setSeed n c = c {optionsSeed = Just n}++data ColorMode = ColorAuto | ColorNever | ColorAlways+  deriving (Eq, Show)++defaultOptions :: Options+defaultOptions = Options False False False False [] Nothing Nothing ColorAuto specdoc False++formatters :: [(String, Formatter)]+formatters = [+    ("specdoc", specdoc)+  , ("progress", progress)+  , ("failed-examples", failed_examples)+  , ("silent", silent)+  ]++formatHelp :: String+formatHelp = unlines (addLineBreaks "use a custom formatter; this can be one of:" ++ map (("   " ++) . fst) formatters)++type Result = Either NoConfig Options++data NoConfig = Help | InvalidArgument String String++data Arg a = Arg {+  argumentName   :: String+, argumentParser :: String -> Maybe a+, argumentSetter :: a -> Options -> Options+}++mkOption :: [Char] -> String -> Arg a -> String -> OptDescr (Result -> Result)+mkOption shortcut name (Arg argName parser setter) help = Option shortcut [name] (ReqArg arg argName) help+  where+    arg :: String -> Result -> Result+    arg input x = x >>= \c -> case parser input of+      Just n -> Right (setter n c)+      Nothing -> Left (InvalidArgument name input)++addLineBreaks :: String -> [String]+addLineBreaks = lineBreaksAt 44++options :: [OptDescr (Result -> Result)]+options = [+    Option   []  ["help"]             (NoArg (const $ Left Help))         (h "display this help and exit")+  , mkOption "m"  "match"             (Arg "PATTERN" return addMatch)     (h "only run examples that match given PATTERN")+  , Option   []  ["color"]            (NoArg setColor)                    (h "colorize the output")+  , Option   []  ["no-color"]         (NoArg setNoColor)                  (h "do not colorize the output")+  , mkOption "f"  "format"            (Arg "FORMATTER" readFormatter setFormatter) formatHelp+  , mkOption "a"  "qc-max-success"    (Arg "N" readMaybe setMaxSuccess)   (h "maximum number of successful tests before a QuickCheck property succeeds")+  , mkOption []   "seed"              (Arg "N" readMaybe setSeed)         (h "used seed for QuickCheck properties")+  , Option   []  ["print-cpu-time"]   (NoArg setPrintCpuTime)             (h "include used CPU time in summary")+  , Option   []  ["dry-run"]          (NoArg setDryRun)                   (h "pretend that everything passed; don't verify anything")+  , Option   []  ["fail-fast"]        (NoArg setFastFail)                 (h "abort on first failure")+  , Option   "r" ["rerun"]            (NoArg  setRerun)                   (h "only rerun examples that previously failed")+  ]+  where+    h = unlines . addLineBreaks++    readFormatter :: String -> Maybe Formatter+    readFormatter = (`lookup` formatters)++    setFormatter :: Formatter -> Options -> Options+    setFormatter f c = c {optionsFormatter = f}++    setPrintCpuTime x = x >>= \c -> return c {optionsPrintCpuTime = True}+    setDryRun       x = x >>= \c -> return c {optionsDryRun       = True}+    setFastFail     x = x >>= \c -> return c {optionsFastFail     = True}+    setRerun        x = x >>= \c -> return c {optionsRerun = True}+    setNoColor      x = x >>= \c -> return c {optionsColorMode = ColorNever}+    setColor        x = x >>= \c -> return c {optionsColorMode = ColorAlways}++undocumentedOptions :: [OptDescr (Result -> Result)]+undocumentedOptions = [+    -- for compatibility with test-framework+    mkOption [] "maximum-generated-tests" (Arg "NUMBER" readMaybe setMaxSuccess) "how many automated tests something like QuickCheck should try, by default"++    -- undocumented for now, as we probably want to change this to produce a+    -- standalone HTML report in the future+  , Option []  ["html"]                    (NoArg setHtml)                    "produce HTML output"++    -- now a noop+  , Option "v" ["verbose"]                 (NoArg id)                         "do not suppress output to stdout when evaluating examples"+  ]+  where+    setHtml :: Result -> Result+    setHtml x = x >>= \c -> return c {optionsHtmlOutput = True}++parseOptions :: Options -> String -> [String] -> Either (ExitCode, String) Options+parseOptions c prog args = case getOpt Permute (options ++ undocumentedOptions) args of+    (opts, [], []) -> case foldl' (flip id) (Right c) opts of+        Left Help                         -> Left (ExitSuccess, usageInfo ("Usage: " ++ prog ++ " [OPTION]...\n\nOPTIONS") options)+        Left (InvalidArgument flag value) -> tryHelp ("invalid argument `" ++ value ++ "' for `--" ++ flag ++ "'\n")+        Right x -> Right x+    (_, _, err:_)  -> tryHelp err+    (_, arg:_, _)  -> tryHelp ("unexpected argument `" ++ arg ++ "'\n")+  where+    tryHelp msg = Left (ExitFailure 1, prog ++ ": " ++ msg ++ "Try `" ++ prog ++ " --help' for more information.\n")
src/Test/Hspec/Runner.hs view
@@ -3,6 +3,7 @@ module Test.Hspec.Runner ( -- * Running a spec   hspec+, hspecResult , hspecWith  -- * Types@@ -12,6 +13,9 @@ , Path , defaultConfig , configAddFilter++-- * Internals+, hspecWithFormatter ) where  import           Control.Monad@@ -26,6 +30,7 @@ import           System.Console.ANSI (hHideCursor, hShowCursor) import qualified Test.QuickCheck as QC import           System.Random (newStdGen)+import           Control.Monad.IO.Class (liftIO)  import           Test.Hspec.Util import           Test.Hspec.Core.Type@@ -33,8 +38,10 @@ import           Test.Hspec.Formatters import           Test.Hspec.Formatters.Internal import           Test.Hspec.FailureReport-import           Test.Hspec.Timer +import           Test.Hspec.Options (Options(..), ColorMode(..), defaultOptions)+import           Test.Hspec.Runner.Eval+ -- | Filter specs by given predicate. -- -- The predicate takes a list of "describe" labels and a "requirement".@@ -42,89 +49,29 @@ filterSpecs p = goSpecs []   where     goSpecs :: [String] -> [SpecTree] -> [SpecTree]-    goSpecs groups = catMaybes . map (goSpec groups)+    goSpecs groups = mapMaybe (goSpec groups)      goSpec :: [String] -> SpecTree -> Maybe SpecTree     goSpec groups spec = case spec of-      SpecItem requirement _ -> guard (p (groups, requirement)) >> return spec+      SpecItem _ requirement _ -> guard (p (groups, requirement)) >> return spec       SpecGroup group specs     -> case goSpecs (groups ++ [group]) specs of         [] -> Nothing         xs -> Just (SpecGroup group xs) --- | Evaluate all examples of a given spec and produce a report.-runFormatter :: Bool -> Config -> Formatter -> [SpecTree] -> FormatM ()-runFormatter useColor c formatter specs = headerFormatter formatter >> zip [0..] specs `each` go []-  where-    -- like forM_, but respects --fast-fail-    each :: [a] -> (a -> FormatM ()) -> FormatM ()-    each []     _ = pure ()-    each (x:xs) f = do-      f x-      fails <- getFailCount-      unless (configFastFail c && fails /= 0) $ do-        xs `each` f--    eval :: IO Result -> FormatM (Either E.SomeException Result)-    eval-      | configDryRun c = \_ -> return (Right Success)-      | otherwise      = liftIO . safeTry . fmap forceResult--    go :: [String] -> (Int, SpecTree) -> FormatM ()-    go rGroups (n, SpecGroup group xs) = do-      exampleGroupStarted formatter n (reverse rGroups) group-      zip [0..] xs `each` go (group : rGroups)-      exampleGroupDone formatter-    go rGroups (_, SpecItem requirement example) = do-      progressHandler <- mkProgressHandler-      result <- eval (example $ Params (configQuickCheckArgs c) progressHandler)-      case result of-        Right Success -> do-          increaseSuccessCount-          exampleSucceeded formatter path-        Right (Pending reason) -> do-          increasePendingCount-          examplePending formatter path reason--        Right (Fail err) -> failed (Right err)-        Left e           -> failed (Left  e)-      where-        path = (groups, requirement)-        groups = reverse rGroups-        failed err = do-          increaseFailCount-          addFailMessage path err-          exampleFailed  formatter path err--        mkProgressHandler-          | useColor = do-              timer <- liftIO $ newTimer 0.05-              return $ \p -> do-                f <- timer-                when f $ do-                  exampleProgress formatter (configHandle c) path p-          | otherwise = return . const $ return ()- -- | Run given spec and write a report to `stdout`. -- Exit with `exitFailure` if at least one spec item fails.------ (see also `hspecWith`) hspec :: Spec -> IO ()-hspec spec = do-  c <- getConfig-  withArgs [] {- do not leak command-line arguments to examples -} $ do-    r <- hspecWith c spec-    unless (summaryFailures r == 0) exitFailure+hspec = hspecWithOptions defaultOptions -handleReRun :: Config -> IO Config-handleReRun c = do-  if configReRun c-    then do-      readFailureReport c-    else do-      return c+-- | This function is used by @hspec-discover@.  It is not part of the public+-- API and may change at any time.+hspecWithFormatter :: IsFormatter a => a -> Spec -> IO ()+hspecWithFormatter formatter spec = do+  f <- toFormatter formatter+  hspecWithOptions defaultOptions {optionsFormatter = f} spec  -- Add a StdGen to configQuickCheckArgs if there is none.  That way the same--- seed is used for all properties.  This helps with --seed and --re-run.+-- seed is used for all properties.  This helps with --seed and --rerun. ensureStdGen :: Config -> IO Config ensureStdGen c = case QC.replay qcArgs of   Nothing -> do@@ -136,15 +83,31 @@  -- | Run given spec with custom options. -- This is similar to `hspec`, but more flexible.+hspecWithOptions :: Options -> Spec -> IO ()+hspecWithOptions opts spec = do+  prog <- getProgName+  args <- getArgs+  c <- getConfig opts prog args+  withArgs [] {- do not leak command-line arguments to examples -} $ do+    r <- hspecWith c spec+    unless (summaryFailures r == 0) exitFailure++-- | Run given spec and returns a summary of the test run. ----- /Note/: `hspecWith` does not exit with `exitFailure` on failing spec items.--- If you need this, you have to check the `Summary` yourself and act+-- /Note/: `hspecResult` does not exit with `exitFailure` on failing spec+-- items.  If you need this, you have to check the `Summary` yourself and act -- accordingly.+hspecResult :: Spec -> IO Summary+hspecResult = hspecWith defaultConfig++-- | Run given spec with custom options and returns a summary of the test run.+--+-- /Note/: `hspecWith` does not exit with `exitFailure` on failing spec+-- items.  If you need this, you have to check the `Summary` yourself and act+-- accordingly. hspecWith :: Config -> Spec -> IO Summary hspecWith c_ spec = do-  -- read failure report on --re-run-  c <- handleReRun c_ >>= ensureStdGen-+  c <- ensureStdGen c_   let formatter = configFormatter c       h = configHandle c       seed = (stdGenToInteger . fst . fromJust . QC.replay . configQuickCheckArgs) c@@ -160,7 +123,11 @@        -- dump failure report       xs <- map failureRecordPath <$> getFailMessages-      liftIO $ writeFailureReport (seed, xs)+      liftIO $ writeFailureReport FailureReport {+          failureReportSeed = seed+        , failureReportMaxSuccess = QC.maxSuccess (configQuickCheckArgs c)+        , failureReportPaths = xs+        }        Summary <$> getTotalCount <*> getFailCount   where@@ -173,7 +140,7 @@     doesUseColor h c = case configColorMode c of       ColorAuto  -> hIsTerminalDevice h       ColorNever -> return False-      ColorAlway -> return True+      ColorAlways -> return True  -- | Summary of a test run. data Summary = Summary {
+ src/Test/Hspec/Runner/Eval.hs view
@@ -0,0 +1,124 @@+module Test.Hspec.Runner.Eval (runFormatter) where++import           Control.Monad+import qualified Control.Exception as E+import           Control.Concurrent++import           Control.Monad.IO.Class (liftIO)++import           Test.Hspec.Util+import           Test.Hspec.Core.Type+import           Test.Hspec.Config+import           Test.Hspec.Formatters+import           Test.Hspec.Formatters.Internal+import           Test.Hspec.Timer+import           Data.Time.Clock.POSIX++-- | Evaluate all examples of a given spec and produce a report.+runFormatter :: Bool -> Config -> Formatter -> [SpecTree] -> FormatM ()+runFormatter useColor c formatter specs = do+  headerFormatter formatter+  chan <- liftIO newChan+  run chan useColor c formatter specs++data Message = Done | Run (FormatM ())++data Report = ReportProgress Progress | ReportResult (Either E.SomeException Result)++run :: Chan Message -> Bool -> Config -> Formatter -> [SpecTree] -> FormatM ()+run chan useColor c formatter specs = do+  liftIO $ do+    forM_ (zip [0..] specs) (queueSpec [])+    writeChan chan Done+  processChan chan (configFastFail c)+  where+    defer = writeChan chan . Run++    queueSpec :: [String] -> (Int, SpecTree) -> IO ()+    queueSpec rGroups (n, SpecGroup group xs) = do+      defer (exampleGroupStarted formatter n (reverse rGroups) group)+      forM_ (zip [0..] xs) (queueSpec (group : rGroups))+      defer (exampleGroupDone formatter)+    queueSpec rGroups (_, SpecItem isParallelizable requirement e) =+      queueExample isParallelizable (reverse rGroups, requirement) e++    queueExample :: Bool -> Path -> (Params -> IO Result) -> IO ()+    queueExample isParallelizable path e+      | isParallelizable = runParallel+      | otherwise = defer runSequentially+      where+        runSequentially :: FormatM ()+        runSequentially = do+          progressHandler <- liftIO (mkProgressHandler reportProgress)+          result <- liftIO (evalExample e progressHandler)+          formatResult formatter path result++        runParallel = do+          mvar <- newEmptyMVar+          _ <- forkIO $ do+            progressHandler <- mkProgressHandler (replaceMVar mvar . ReportProgress)+            result <- evalExample e progressHandler+            replaceMVar mvar (ReportResult result)+          defer (evalReport mvar)+          where+            evalReport :: MVar Report -> FormatM ()+            evalReport mvar = do+              r <- liftIO (takeMVar mvar)+              case r of+                ReportProgress p -> do+                  liftIO $ reportProgress p+                  evalReport mvar+                ReportResult result -> formatResult formatter path result++        reportProgress :: (Int, Int) -> IO ()+        reportProgress = exampleProgress formatter (configHandle c) path++    mkProgressHandler :: (a -> IO ()) -> IO (a -> IO ())+    mkProgressHandler report+      | useColor = every 0.05 report+      | otherwise = return . const $ return ()++    evalExample :: (Params -> IO Result) -> (Progress -> IO ()) -> IO (Either E.SomeException Result)+    evalExample e progressHandler+      | configDryRun c = return (Right Success)+      | otherwise      = (safeTry . fmap forceResult) (e $ Params (configQuickCheckArgs c) progressHandler)++replaceMVar :: MVar a -> a -> IO ()+replaceMVar mvar p = tryTakeMVar mvar >> putMVar mvar p++processChan :: Chan Message -> Bool -> FormatM ()+processChan chan fastFail = go+  where+    go = do+      m <- liftIO (readChan chan)+      case m of+        Run action -> do+          action+          fails <- getFailCount+          unless (fastFail && fails /= 0) go+        Done -> return ()++formatResult :: Formatter -> ([String], String) -> Either E.SomeException Result -> FormatM ()+formatResult formatter path result = do+  case result of+    Right Success -> do+      increaseSuccessCount+      exampleSucceeded formatter path+    Right (Pending reason) -> do+      increasePendingCount+      examplePending formatter path reason+    Right (Fail err) -> failed (Right err)+    Left e           -> failed (Left  e)+  where+    failed err = do+      increaseFailCount+      addFailMessage path err+      exampleFailed  formatter path err++-- | Execute given action at most every specified number of seconds.+every :: POSIXTime -> (a -> IO ()) -> IO (a -> IO ())+every seconds action = do+  timer <- newTimer seconds+  return $ \a -> do+    r <- timer+    when r (action a)
+ test/Helper.hs view
@@ -0,0 +1,58 @@+module Helper (+  module Test.Hspec.Meta+, module Test.QuickCheck+, module Control.Applicative+, module System.IO.Silently+, sleep+, timeout+, defaultParams+, captureLines+, normalizeSummary++, shouldStartWith+, shouldEndWith+, shouldContain+) where++import           Control.Applicative+import           Test.Hspec.Meta+import           Test.QuickCheck hiding (Result(..))++import qualified Test.Hspec.Core as H+import qualified Test.Hspec.Runner as H+import           Data.List+import           Data.Char+import           System.IO.Silently+import           Data.Time.Clock.POSIX+import           Control.Concurrent+import qualified System.Timeout as System++captureLines :: IO a -> IO [String]+captureLines = fmap lines . capture_++shouldContain :: (Eq a, Show a) => [a] -> [a] -> Expectation+x `shouldContain` y = x `shouldSatisfy` isInfixOf y++shouldStartWith :: (Eq a, Show a) => [a] -> [a] -> Expectation+x `shouldStartWith` y = x `shouldSatisfy` isPrefixOf y++shouldEndWith :: (Eq a, Show a) => [a] -> [a] -> Expectation+x `shouldEndWith` y = x `shouldSatisfy` isSuffixOf y++-- replace times in summary with zeroes+normalizeSummary :: [String] -> [String]+normalizeSummary xs = map f xs+  where+    f x | "Finished in " `isPrefixOf` x = map g x+        | otherwise = x+    g x | isNumber x = '0'+        | otherwise  = x++defaultParams :: H.Params+defaultParams = H.Params (H.configQuickCheckArgs H.defaultConfig) (const $ return ())++sleep :: POSIXTime -> IO ()+sleep = threadDelay . floor . (* 1000000)++timeout :: POSIXTime -> IO a -> IO (Maybe a)+timeout = System.timeout . floor . (* 1000000)
− test/SpecHelper.hs
@@ -1,37 +0,0 @@-module SpecHelper where--import qualified Test.Hspec.Core as H-import qualified Test.Hspec.Runner as H-import           Data.List-import           Data.Char-import           Test.Hspec.Meta-import           System.IO.Silently-import           Data.Time.Clock.POSIX-import           Control.Concurrent--captureLines :: IO a -> IO [String]-captureLines = fmap lines . capture_--shouldContain :: (Eq a, Show a) => [a] -> [a] -> Expectation-x `shouldContain` y = x `shouldSatisfy` isInfixOf y--shouldStartWith :: (Eq a, Show a) => [a] -> [a] -> Expectation-x `shouldStartWith` y = x `shouldSatisfy` isPrefixOf y--shouldEndWith :: (Eq a, Show a) => [a] -> [a] -> Expectation-x `shouldEndWith` y = x `shouldSatisfy` isSuffixOf y---- replace times in summary with zeroes-normalizeSummary :: [String] -> [String]-normalizeSummary xs = map f xs-  where-    f x | "Finished in " `isPrefixOf` x = map g x-        | otherwise = x-    g x | isNumber x = '0'-        | otherwise  = x--defaultParams :: H.Params-defaultParams = H.Params (H.configQuickCheckArgs H.defaultConfig) (const $ return ())--sleep :: POSIXTime -> IO ()-sleep t = threadDelay (floor $ t * 1000000)
test/Test/Hspec/CompatSpec.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE DeriveDataTypeable #-} module Test.Hspec.CompatSpec (main, spec) where -import           Test.Hspec.Meta+import           Helper  import           Test.Hspec.Compat import           Data.Typeable
test/Test/Hspec/Core/TypeSpec.hs view
@@ -1,13 +1,10 @@ module Test.Hspec.Core.TypeSpec (main, spec) where -import           Test.Hspec.Meta-import           SpecHelper-import           Test.QuickCheck+import           Helper import           Mock import           Data.List-import           System.IO.Silently- import           Control.Exception (AsyncException(..), throwIO)+ import qualified Test.Hspec.Core.Type as H hiding (describe, it) import qualified Test.Hspec as H import qualified Test.Hspec.Runner as H@@ -103,7 +100,7 @@         mockCounter e `shouldReturn` 100        it "can be used with expecatations/HUnit assertions" $ do-        silence . H.hspecWith H.defaultConfig $ do+        silence . H.hspecResult $ do           H.describe "readIO" $ do             H.it "is inverse to show" $ property $ \x -> do               (readIO . show) x `shouldReturn` (x :: Int)
test/Test/Hspec/FailureReportSpec.hs view
@@ -1,9 +1,8 @@ module Test.Hspec.FailureReportSpec (main, spec) where -import           Test.Hspec.Meta+import           Helper  import           System.IO-import           System.IO.Silently import           Test.Hspec.FailureReport import           GHC.Paths (ghc) import           System.Process@@ -20,7 +19,7 @@       r `shouldBe` "WARNING: Could not write environment variable HSPEC_FAILURES (some error)\n"    -- GHCi needs to keep the environment on :reload, so that we can store-  -- failures there.  Otherwise --re-run would not be very useful.  So we add a+  -- failures there.  Otherwise --rerun would not be very useful.  So we add a   -- test for that.   describe "GHCi" $ do     it "keeps environment variables on :reload" $ do
test/Test/Hspec/FormattersSpec.hs view
@@ -1,10 +1,8 @@ {-# LANGUAGE CPP #-} module Test.Hspec.FormattersSpec (main, spec) where -import           Test.Hspec.Meta+import           Helper -import           SpecHelper-import           System.IO.Silently (capture) import qualified Test.Hspec as H import qualified Test.Hspec.Core as H (Result(..)) import qualified Test.Hspec.Runner as H@@ -198,22 +196,22 @@   -- colorized output, hence the following tests do not work on Windows. #ifndef mingw32_HOST_OS   it "shows summary in green if there are no failures" $ do-    r <- captureLines $ H.hspecWith H.defaultConfig {H.configColorMode = H.ColorAlway} $ do+    r <- captureLines $ H.hspecWith H.defaultConfig {H.configColorMode = H.ColorAlways} $ do       H.it "foobar" True     r `shouldSatisfy` any (== (green ++ "1 example, 0 failures" ++ reset))    it "shows summary in yellow if there are pending examples" $ do-    r <- captureLines $ H.hspecWith H.defaultConfig {H.configColorMode = H.ColorAlway} $ do+    r <- captureLines $ H.hspecWith H.defaultConfig {H.configColorMode = H.ColorAlways} $ do       H.it "foobar" H.pending     r `shouldSatisfy` any (== (yellow ++ "1 example, 0 failures, 1 pending" ++ reset))    it "shows summary in red if there are failures" $ do-    r <- captureLines $ H.hspecWith H.defaultConfig {H.configColorMode = H.ColorAlway} $ do+    r <- captureLines $ H.hspecWith H.defaultConfig {H.configColorMode = H.ColorAlways} $ do       H.it "foobar" False     r `shouldSatisfy` any (== (red ++ "1 example, 1 failure" ++ reset))    it "shows summary in red if there are both failures and pending examples" $ do-    r <- captureLines $ H.hspecWith H.defaultConfig {H.configColorMode = H.ColorAlway} $ do+    r <- captureLines $ H.hspecWith H.defaultConfig {H.configColorMode = H.ColorAlways} $ do       H.it "foo" False       H.it "bar" H.pending     r `shouldSatisfy` any (== (red ++ "2 examples, 1 failure, 1 pending" ++ reset))
test/Test/Hspec/HUnitSpec.hs view
@@ -1,9 +1,6 @@ module Test.Hspec.HUnitSpec (main, spec) where -import           Test.Hspec.Meta-import           SpecHelper (captureLines)-import           Control.Applicative-import           System.IO.Silently+import           Helper  import qualified Test.Hspec as H import qualified Test.Hspec.Runner as H@@ -27,7 +24,7 @@         go :: SpecTree -> Tree         go x = case x of           SpecGroup s xs  -> Group s (map go xs)-          SpecItem  s _   -> Example s+          SpecItem _ s _  -> Example s  spec :: Spec spec = do@@ -55,30 +52,28 @@    describe "HUnit TestCase as an example (deprecated!)" $ do     it "is specified with the HUnit `TestCase` data constructor" $ TestCase $ do-      silence . runSpec $ do+      silence . H.hspecResult $ do         H.it "some behavior" (TestCase $ "foo" @?= "bar")         H.it "some behavior" (TestCase $ "foo" @?= "foo")       `shouldReturn` H.Summary 2 1      it "is the assumed example for IO() actions" $ do-      silence . runSpec $ do+      silence . H.hspecResult $ do         H.it "some behavior" ("foo" @?= "bar")         H.it "some behavior" ("foo" @?= "foo")       `shouldReturn` H.Summary 2 1      it "will show the failed assertion text if available (e.g. assertBool)" $ do       let assertionText = "some assertion text"-      r <- captureLines . runSpec $ do+      r <- captureLines . H.hspecResult $ do         H.describe "foo" $ do           H.it "bar" (assertFailure assertionText)       r `shouldSatisfy` any (== assertionText)      it "will show the failed assertion expected and actual values if available (e.g. assertEqual)" $ do-      r <- captureLines . runSpec $ do+      r <- captureLines . H.hspecResult $ do         H.describe "foo" $ do           H.it "bar" (assertEqual "trivial" (1::Int) 2)       assertBool "should find assertion text" $ any (=="trivial") r       assertBool "should find 'expected: 1'"  $ any (=="expected: 1") r       assertBool "should find ' but got: 2'"  $ any (==" but got: 2") r-  where-    runSpec = H.hspecWith H.defaultConfig
+ test/Test/Hspec/OptionsSpec.hs view
@@ -0,0 +1,26 @@+module Test.Hspec.OptionsSpec (main, spec) where++import           Helper++import           Test.Hspec.Options hiding (parseOptions)+import qualified Test.Hspec.Options as Options++main :: IO ()+main = hspec spec++spec :: Spec+spec = do+  describe "parseOptions" $ do++    let parseOptions = Options.parseOptions defaultOptions "my-spec"++    it "sets optionsColorMode to ColorAuto" $ do+      optionsColorMode <$> parseOptions [] `shouldBe` Right ColorAuto++    context "with --no-color" $ do+      it "sets optionsColorMode to ColorNever" $ do+        optionsColorMode <$> parseOptions ["--no-color"] `shouldBe` Right ColorNever++    context "with --color" $ do+      it "sets optionsColorMode to ColorAlways" $ do+        optionsColorMode <$> parseOptions ["--color"] `shouldBe` Right ColorAlways
test/Test/Hspec/QuickCheckSpec.hs view
@@ -1,7 +1,6 @@ module Test.Hspec.QuickCheckSpec (main, spec) where -import           Test.Hspec.Meta-import           System.IO.Silently+import           Helper  import qualified Test.Hspec as H import qualified Test.Hspec.Runner as H@@ -14,7 +13,7 @@ spec = do   describe "prop" $ do     it "is a shortcut to use properties as examples" $ do-      silence . H.hspecWith H.defaultConfig $ do+      silence . H.hspecResult $ do         H.describe "read" $ do           H.prop "is inverse to show" $ \x -> (read . show) x == (x :: Int)       `shouldReturn` H.Summary 1 0
test/Test/Hspec/RunnerSpec.hs view
@@ -1,22 +1,18 @@ module Test.Hspec.RunnerSpec (main, spec) where -import           Test.Hspec.Meta-import           System.IO.Silently+import           Helper import           System.IO (stderr)-import           Control.Applicative-import           Control.Monad (unless)+import           Control.Monad import           System.Environment (withArgs, withProgName, getArgs) import           System.Exit import qualified Control.Exception as E-import           SpecHelper import           Mock import           System.SetEnv import           Test.Hspec.Util (getEnv)-import           Test.QuickCheck +import qualified Test.Hspec as H import qualified Test.Hspec.Runner as H import qualified Test.Hspec.Core as H (Result(..))-import qualified Test.Hspec as H (describe, it, pending) import qualified Test.Hspec.Formatters as H (silent)  ignoreExitCode :: IO () -> IO ()@@ -56,6 +52,83 @@           , "Try `myspec --help' for more information."           ] +    it "stores a failure report in the environment" $ do+      silence . ignoreExitCode . withArgs ["--seed", "23"] . H.hspec $ do+        H.describe "foo" $ do+          H.describe "bar" $ do+            H.it "example 1" True+            H.it "example 2" False+        H.describe "baz" $ do+          H.it "example 3" False+      getEnv "HSPEC_FAILURES" `shouldReturn` Just ("FailureReport {"+        ++ "failureReportSeed = 23, "+        ++ "failureReportMaxSuccess = 100, "+        ++ "failureReportPaths = [([\"foo\",\"bar\"],\"example 2\"),([\"baz\"],\"example 3\")]}")++    describe "with --rerun" $ do+      let runSpec = (captureLines . ignoreExitCode . H.hspec) $ do+            H.it "example 1" True+            H.it "example 2" False+            H.it "example 3" False+            H.it "example 4" True+            H.it "example 5" False++      it "reruns examples that previously failed" $ do+        r0 <- runSpec+        r0 `shouldSatisfy` elem "5 examples, 3 failures"++        r1 <- withArgs ["--rerun"] runSpec+        r1 `shouldSatisfy` elem "3 examples, 3 failures"++      it "reuses the same seed" $ do+        let runSpec_ = (captureLines . ignoreExitCode . H.hspec) $ do+              H.it "foo" $ property $ (/= (26 :: Integer))++        r0 <- withArgs ["--seed", "2413421499272008081"] runSpec_+        r0 `shouldContain` [+            "Falsifiable (after 66 tests): "+          , "26"+          ]++        r1 <- withArgs ["-r"] runSpec_+        r1 `shouldContain` [+            "Falsifiable (after 66 tests): "+          , "26"+          ]++      it "reuses same --qc-max-success" $ do+        silence . ignoreExitCode . withArgs ["--qc-max-success", "23"] . H.hspec $ do+          H.it "foo" False++        m <- newMock+        silence . withArgs ["--rerun"] . H.hspec $ do+          H.it "foo" $ property $ do+            mockAction m+        mockCounter m `shouldReturn` 23++      context "when there is no failure report in the environment" $ do+        it "runs everything" $ do+          unsetEnv "HSPEC_FAILURES"+          r <- hSilence [stderr] $ withArgs ["-r"] runSpec+          r `shouldSatisfy` elem "5 examples, 3 failures"++        it "prints a warning to stderr" $ do+          unsetEnv "HSPEC_FAILURES"+          r <- hCapture_ [stderr] $ withArgs ["-r"] runSpec+          r `shouldBe` "WARNING: Could not read environment variable HSPEC_FAILURES; `--rerun' is ignored!\n"++      context "when parsing of failure report fails" $ do+        it "runs everything" $ do+          setEnv "HSPEC_FAILURES" "some invalid report"+          r <- hSilence [stderr] $ withArgs ["-r"] runSpec+          r `shouldSatisfy` elem "5 examples, 3 failures"++        it "prints a warning to stderr" $ do+          setEnv "HSPEC_FAILURES" "some invalid report"+          r <- hCapture [stderr] $ withArgs ["-r"] runSpec+          fst r `shouldBe` "WARNING: Could not read environment variable HSPEC_FAILURES; `--rerun' is ignored!\n"++     it "does not leak command-line flags to examples" $ do       silence . withArgs ["--verbose"] $ do         H.hspec $ do@@ -198,17 +271,28 @@             H.it "foo" True           r `shouldContain` "invalid argument `foo' for `--format'" -    context "with --maximum-generated-tests=n" $ do-      it "tries QuickCheck properties n times" $ do+    context "with --qc-max-success" $ do+      it "tries QuickCheck properties specified number of times" $ do         m <- newMock-        silence . withArgs ["--qc-max-succes", "23"] . H.hspec $ do+        silence . withArgs ["--qc-max-success", "23"] . H.hspec $ do           H.it "foo" $ property $ do             mockAction m         mockCounter m `shouldReturn` 23 +      context "when run with --rerun" $ do+        it "takes precedence" $ do+          silence . ignoreExitCode . withArgs ["--qc-max-success", "23"] . H.hspec $ do+            H.it "foo" False++          m <- newMock+          silence . withArgs ["--rerun", "--qc-max-success", "42"] . H.hspec $ do+            H.it "foo" $ property $ do+              mockAction m+          mockCounter m `shouldReturn` 42+       context "when given an invalid argument" $ do         it "prints an error message to stderr" $ do-          r <- hCapture_ [stderr] . ignoreExitCode . withArgs ["--qc-max-succes", "foo"] . H.hspec $ do+          r <- hCapture_ [stderr] . ignoreExitCode . withArgs ["--qc-max-success", "foo"] . H.hspec $ do             H.it "foo" True           r `shouldContain` "invalid argument `foo' for `--qc-max-success'" @@ -222,7 +306,7 @@           , "26"           ] -      context "when run with --re-run" $ do+      context "when run with --rerun" $ do         it "takes precedence" $ do           let runSpec args = capture_ . ignoreExitCode . withArgs args . H.hspec $ do                 H.it "foo" $@@ -233,7 +317,7 @@           r1 <- runSpec ["--seed", "42"]           r1 `shouldContain` "(after 48 tests)" -          r2 <- runSpec ["--re-run", "--seed", "23"]+          r2 <- runSpec ["--rerun", "--seed", "23"]           r2 `shouldContain` "(after 88 tests)"        context "when given an invalid argument" $ do@@ -272,73 +356,9 @@           H.it "foo" False         r `shouldContain` "<span class=\"hspec-failure\">- foo" -  describe "hspec (experimental features)" $ do-    it "stores a failure report in environment" $ do-      silence . ignoreExitCode . withArgs ["--seed", "23"] . H.hspec $ do-        H.describe "foo" $ do-          H.describe "bar" $ do-            H.it "example 1" True-            H.it "example 2" False-        H.describe "baz" $ do-          H.it "example 3" False-      getEnv "HSPEC_FAILURES" `shouldReturn` Just "(23,[([\"foo\",\"bar\"],\"example 2\"),([\"baz\"],\"example 3\")])"--    describe "with --re-run" $ do-      let runSpec = (captureLines . ignoreExitCode . H.hspec) $ do-            H.it "example 1" True-            H.it "example 2" False-            H.it "example 3" False-            H.it "example 4" True-            H.it "example 5" False--      it "re-runs examples that previously failed" $ do-        r0 <- runSpec-        r0 `shouldSatisfy` elem "5 examples, 3 failures"--        r1 <- withArgs ["-r"] runSpec-        r1 `shouldSatisfy` elem "3 examples, 3 failures"--      it "reuses the same seed" $ do-        let runSpec_ = (captureLines . ignoreExitCode . H.hspec) $ do-              H.it "foo" $ property $ (/= (26 :: Integer))--        r0 <- withArgs ["--seed", "2413421499272008081"] runSpec_-        r0 `shouldContain` [-            "Falsifiable (after 66 tests): "-          , "26"-          ]--        r1 <- withArgs ["-r"] runSpec_-        r1 `shouldContain` [-            "Falsifiable (after 66 tests): "-          , "26"-          ]--      context "when there is no failure report in the environment" $ do-        it "runs everything" $ do-          unsetEnv "HSPEC_FAILURES"-          r <- hSilence [stderr] $ withArgs ["-r"] runSpec-          r `shouldSatisfy` elem "5 examples, 3 failures"--        it "prints a warning to stderr" $ do-          unsetEnv "HSPEC_FAILURES"-          r <- hCapture_ [stderr] $ withArgs ["-r"] runSpec-          r `shouldBe` "WARNING: Could not read environment variable HSPEC_FAILURES; `--re-run' is ignored!\n"--      context "when parsing of failure report fails" $ do-        it "runs everything" $ do-          setEnv "HSPEC_FAILURES" "some invalid report"-          r <- hSilence [stderr] $ withArgs ["-r"] runSpec-          r `shouldSatisfy` elem "5 examples, 3 failures"--        it "prints a warning to stderr" $ do-          setEnv "HSPEC_FAILURES" "some invalid report"-          r <- hCapture [stderr] $ withArgs ["-r"] runSpec-          fst r `shouldBe` "WARNING: Could not read environment variable HSPEC_FAILURES; `--re-run' is ignored!\n"--  describe "hspecWith" $ do+  describe "hspecResult" $ do     it "returns a summary of the test run" $ do-      silence . H.hspecWith H.defaultConfig $ do+      silence . H.hspecResult $ do         H.it "foo" True         H.it "foo" False         H.it "foo" False@@ -347,12 +367,12 @@       `shouldReturn` H.Summary 5 2      it "treats uncaught exceptions as failure" $ do-      silence . H.hspecWith H.defaultConfig  $ do+      silence . H.hspecResult  $ do         H.it "foobar" (E.throwIO (E.ErrorCall "foobar") >> pure ())       `shouldReturn` H.Summary 1 1      it "uses the specdoc formatter by default" $ do-      _:r:_ <- captureLines . H.hspecWith H.defaultConfig $ do+      _:r:_ <- captureLines . H.hspecResult $ do         H.describe "Foo.Bar" $ do           H.it "some example" True       r `shouldBe` "Foo.Bar"@@ -364,6 +384,14 @@       r `shouldBe` ""      it "does not let escape error thunks from failure messages" $ do-      r <- silence . H.hspecWith H.defaultConfig $ do+      r <- silence . H.hspecResult $ do         H.it "some example" (H.Fail $ "foobar" ++ undefined)       r `shouldBe` H.Summary 1 1++    it "runs specs in parallel" $ do+      let n = 10+          t = 0.01+          dt = t * (fromIntegral n / 2)+      r <- timeout dt . silence . H.hspecResult . H.parallel $ do+        replicateM_ n (H.it "foo" $ sleep t)+      r `shouldBe` Just (H.Summary n 0)
test/Test/Hspec/TimerSpec.hs view
@@ -1,7 +1,6 @@ module Test.Hspec.TimerSpec (main, spec) where -import           Test.Hspec.Meta-import           SpecHelper+import           Helper  import           Test.Hspec.Timer 
test/Test/Hspec/UtilSpec.hs view
@@ -1,15 +1,13 @@ {-# OPTIONS_GHC -fno-warn-orphans #-} module Test.Hspec.UtilSpec (main, spec) where -import           Test.Hspec.Meta-import           Test.QuickCheck+import           Helper import           Data.Int (Int32) import           System.Random (StdGen)- import qualified Control.Exception as E-import           Test.Hspec.Util- import           System.SetEnv++import           Test.Hspec.Util  main :: IO () main = hspec spec
test/Test/HspecSpec.hs view
@@ -1,16 +1,11 @@ module Test.HspecSpec (main, spec) where -import           Test.Hspec.Meta-import           SpecHelper+import           Helper import           Data.List (isPrefixOf) -import           Control.Applicative- import           Test.Hspec.Core (SpecTree(..), Result(..), runSpecM) import qualified Test.Hspec as H-import qualified Test.Hspec.Runner as H (hspecWith)-import           Test.Hspec.Runner (defaultConfig)-+import qualified Test.Hspec.Runner as H (hspecResult) main :: IO () main = hspec spec @@ -22,7 +17,7 @@         H.it "foo" H.pending       r `shouldSatisfy` any (== "     # PENDING: No reason given") -  describe "pending" $ do+  describe "pendingWith" $ do     it "specifies a pending example with a reason for why it's pending" $ do       r <- runSpec $ do         H.it "foo" $ do@@ -44,7 +39,7 @@       length r `shouldBe` 3      it "can be nested" $ do-      let [SpecGroup foo [SpecGroup bar [SpecItem baz _]]] = runSpecM $ do+      let [SpecGroup foo [SpecGroup bar [SpecItem _ baz _]]] = runSpecM $ do             H.describe "foo" $ do               H.describe "bar" $ do                 H.it "baz" True@@ -57,16 +52,16 @@    describe "it" $ do     it "takes a description of a desired behavior" $ do-      let [SpecItem d _] = runSpecM (H.it "whatever" True)+      let [SpecItem _ d _] = runSpecM (H.it "whatever" True)       d `shouldBe` "whatever"      it "takes an example of that behavior" $ do-      let [SpecItem _ e] = runSpecM (H.it "whatever" True)+      let [SpecItem _ _ e] = runSpecM (H.it "whatever" True)       e defaultParams `shouldReturn` Success      context "when no description is given" $ do       it "uses a default description" $ do-        let [SpecItem d _] = runSpecM (H.it "" True)+        let [SpecItem _ d _] = runSpecM (H.it "" True)         d `shouldBe` "(unspecified behavior)"    describe "example" $ do@@ -75,6 +70,18 @@         H.it "foo" $ H.example $ do           pure ()       r `shouldSatisfy` any (== "1 example, 0 failures")++  describe "parallel" $ do+    it "marks examples for parallel execution" $ do+      let [SpecItem isParallelizable _ _] = runSpecM . H.parallel $ H.it "whatever" True+      isParallelizable `shouldBe` True++    it "is applied recursively" $ do+      let [SpecGroup _ [SpecGroup _ [SpecItem isParallelizable _ _]]] = runSpecM . H.parallel $ do+            H.describe "foo" $ do+              H.describe "bar" $ do+                H.it "baz" True+      isParallelizable `shouldBe` True   where     runSpec :: H.Spec -> IO [String]-    runSpec = captureLines . H.hspecWith defaultConfig+    runSpec = captureLines . H.hspecResult