packages feed

hspec 1.2.0 → 1.2.0.1

raw patch · 22 files changed

+1144/−1145 lines, 22 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

Files

− Test/Hspec.hs
@@ -1,129 +0,0 @@-{-# OPTIONS_HADDOCK prune #-}--- |--- Hspec is a Behaviour-Driven Development tool for Haskell programmers. BDD is--- an approach to software development that combines Test-Driven Development,--- Domain Driven Design, and Acceptance Test-Driven Planning. Hspec helps you--- do the TDD part of that equation, focusing on the documentation and design--- aspects of TDD.------ Hspec (and the preceding intro) are based on the Ruby library RSpec. Much of--- what applies to RSpec also applies to Hspec. Hspec ties together--- /descriptions/ of behavior and /examples/ of that behavior. The examples can--- also be run as tests and the output summarises what needs to be implemented.------ NOTE: There is a monadic and a non-monadic API.  This is the documentation--- for the non-monadic API.  The monadic API is more stable, so you may prefer--- it over this one.  For documentation on the monadic API look at--- "Test.Hspec.Monadic".--module Test.Hspec {-# WARNING "This module will re-export Test.Hspec.Monadic in the future.  Either use Test.Hspec.Core as a drop-in replacement, or migrate your code to the monadic API!" #-} (---- * Introduction--- $intro---- * Types-  Spec-, Specs-, Example-, Pending---- * Defining a spec-, describe-, it-, pending---- * Running a spec-, hspec-, hspecB-, hHspec-, Summary (..)-------- deprecated functions-, descriptions-, hspecX----) where--import           Test.Hspec.Core------- $intro------ The three functions you'll use the most are 'hspecX', 'describe', and 'it'.--- Here is an example of functions that format and unformat phone numbers and--- the specs for them.------ > import Test.Hspec--- > import Test.Hspec.QuickCheck--- > import Test.Hspec.HUnit ()--- > import Test.QuickCheck--- > import Test.HUnit--- >--- > main = hspecX mySpecs------ Since the specs are often used to tell you what to implement, it's best to--- start with undefined functions. Once we have some specs, then you can--- implement each behavior one at a time, ensuring that each behavior is met--- and there is no undocumented behavior.------ > unformatPhoneNumber :: String -> String--- > unformatPhoneNumber number = undefined--- >--- > formatPhoneNumber :: String -> String--- > formatPhoneNumber number = undefined------ The 'describe' function takes a list of behaviors and examples bound--- together with the 'it' function------ > mySpecs = [describe "unformatPhoneNumber" [------ A boolean expression can act as a behavior's example.------ >   it "removes dashes, spaces, and parenthesies" $--- >     unformatPhoneNumber "(555) 555-1234" == "5555551234"--- >   ,------ The 'pending' function marks a behavior as pending an example. The example--- doesn't count as failing.------ >   it "handles non-US phone numbers" $--- >     pending "need to look up how other cultures format phone numbers"--- >   ,------ An HUnit 'Test.HUnit.Test' can act as a behavior's example. (must import--- "Test.Hspec.HUnit")------ >   it "removes the \"ext\" prefix of the extension" $ TestCase $ do--- >     let expected = "5555551234135"--- >         actual   = unformatPhoneNumber "(555) 555-1234 ext 135"--- >     expected @?= actual--- >   ,------ An @IO()@ action is treated like an HUnit 'TestCase'. (must import--- "Test.Hspec.HUnit")------ >   it "converts letters to numbers" $ do--- >     let expected = "6862377"--- >         actual   = unformatPhoneNumber "NUMBERS"--- >     actual @?= expected--- >   ,------ The 'property' function allows a QuickCheck property to act as an example.--- (must import "Test.Hspec.QuickCheck")------ >   it "can add and remove formatting without changing the number" $ property $--- >     forAll phoneNumber $ \n -> unformatPhoneNumber (formatPhoneNumber n) == n--- >   ]]--- >--- > phoneNumber :: Gen String--- > phoneNumber = do--- >   n <- elements [7,10,11,12,13,14,15]--- >   vectorOf n (elements "0123456789")
− Test/Hspec/Core.hs
@@ -1,144 +0,0 @@-{-# OPTIONS_HADDOCK prune #-}--- |--- Hspec is a Behaviour-Driven Development tool for Haskell programmers. BDD is--- an approach to software development that combines Test-Driven Development,--- Domain Driven Design, and Acceptance Test-Driven Planning. Hspec helps you--- do the TDD part of that equation, focusing on the documentation and design--- aspects of TDD.------ Hspec (and the preceding intro) are based on the Ruby library RSpec. Much of--- what applies to RSpec also applies to Hspec. Hspec ties together--- /descriptions/ of behavior and /examples/ of that behavior. The examples can--- also be run as tests and the output summarises what needs to be implemented.------ NOTE: There is a monadic and a non-monadic API.  This is the documentation--- for the non-monadic API.  The monadic API is more stable, so you may prefer--- it over this one.  For documentation on the monadic API look at--- "Test.Hspec.Monadic".--module Test.Hspec.Core (----- * Introduction--- $intro---- * Types-  Spec-, Specs-, Example (..)-, Pending---- * Defining a spec-, describe-, it-, pending---- * Running a spec-, hspec-, hspecB-, hHspec-, Summary (..)---- * Internals-, quantify-, Result (..)---- deprecated stuff-, descriptions-, hspecX-, AnyExample-, safeEvaluateExample-, UnevaluatedSpec-) where--import           Test.Hspec.Internal hiding (safeEvaluateExample)-import qualified Test.Hspec.Internal as Internal-import           Test.Hspec.Pending-import           Test.Hspec.Runner---- $intro------ The three functions you'll use the most are 'hspecX', 'describe', and 'it'.--- Here is an example of functions that format and unformat phone numbers and--- the specs for them.------ > import Test.Hspec--- > import Test.Hspec.QuickCheck--- > import Test.Hspec.HUnit ()--- > import Test.QuickCheck--- > import Test.HUnit--- >--- > main = hspecX mySpecs------ Since the specs are often used to tell you what to implement, it's best to--- start with undefined functions. Once we have some specs, then you can--- implement each behavior one at a time, ensuring that each behavior is met--- and there is no undocumented behavior.------ > unformatPhoneNumber :: String -> String--- > unformatPhoneNumber number = undefined--- >--- > formatPhoneNumber :: String -> String--- > formatPhoneNumber number = undefined------ The 'describe' function takes a list of behaviors and examples bound--- together with the 'it' function------ > mySpecs = [describe "unformatPhoneNumber" [------ A boolean expression can act as a behavior's example.------ >   it "removes dashes, spaces, and parenthesies" $--- >     unformatPhoneNumber "(555) 555-1234" == "5555551234"--- >   ,------ The 'pending' function marks a behavior as pending an example. The example--- doesn't count as failing.------ >   it "handles non-US phone numbers" $--- >     pending "need to look up how other cultures format phone numbers"--- >   ,------ An HUnit 'Test.HUnit.Test' can act as a behavior's example. (must import--- "Test.Hspec.HUnit")------ >   it "removes the \"ext\" prefix of the extension" $ TestCase $ do--- >     let expected = "5555551234135"--- >         actual   = unformatPhoneNumber "(555) 555-1234 ext 135"--- >     expected @?= actual--- >   ,------ An @IO()@ action is treated like an HUnit 'TestCase'. (must import--- "Test.Hspec.HUnit")------ >   it "converts letters to numbers" $ do--- >     let expected = "6862377"--- >         actual   = unformatPhoneNumber "NUMBERS"--- >     actual @?= expected--- >   ,------ The 'property' function allows a QuickCheck property to act as an example.--- (must import "Test.Hspec.QuickCheck")------ >   it "can add and remove formatting without changing the number" $ property $--- >     forAll phoneNumber $ \n -> unformatPhoneNumber (formatPhoneNumber n) == n--- >   ]]--- >--- > phoneNumber :: Gen String--- > phoneNumber = do--- >   n <- elements [7,10,11,12,13,14,15]--- >   vectorOf n (elements "0123456789")--{-# DEPRECATED UnevaluatedSpec "use Spec instead" #-}-type UnevaluatedSpec = Spec--{-# DEPRECATED descriptions "this is no longer needed, and will be removed in a future release" #-}-descriptions :: Specs -> Specs-descriptions = id--{-# DEPRECATED AnyExample "This will be removed with the next major release.  If you still need this, raise your voice!" #-}-type AnyExample  = IO Result--{-# DEPRECATED safeEvaluateExample "This will be removed with the next major release.  If you still need this, raise your voice!" #-}-safeEvaluateExample :: AnyExample -> IO Result-safeEvaluateExample = Internal.safeEvaluateExample
− Test/Hspec/Formatters.hs
@@ -1,146 +0,0 @@--- | This module contains formatters that take a set of specs and write to a given handle.--- They follow a structure similar to RSpec formatters.----module Test.Hspec.Formatters (---- * Formatters-  silent-, specdoc-, progress-, failed_examples---- * Implementing a custom Formatter--- |--- A formatter is a set of actions.  Each action is evaluated when a certain--- situation is encountered during a test run.------ Actions live in the `FormatM` monad.  It provides access to the runner state--- and primitives for appending to the generated report.-, Formatter (..)-, FormatM---- ** Accessing the runner state-, getSuccessCount-, getPendingCount-, getFailCount-, getTotalCount-, getFailMessages-, getCPUTime-, getRealTime---- ** Appending to the gerenated report-, write-, writeLine---- ** Dealing with colors-, withSuccessColor-, withPendingColor-, withFailColor-) where--import           Data.Maybe-import           Test.Hspec.Internal (quantify)-import           Data.List (intersperse)-import           Text.Printf-import           Control.Monad (unless)-import           Control.Applicative---- We use an explicit import list for "Test.Hspec.Formatters.Internal", to make--- sure, that we only use the public API to implement formatters.------ Everything imported here has to be re-exported, so that users can implement--- their own formatters.-import Test.Hspec.Formatters.Internal (-    Formatter (..)-  , FormatM--  , getSuccessCount-  , getPendingCount-  , getFailCount-  , getTotalCount-  , getFailMessages-  , getCPUTime-  , getRealTime--  , write-  , writeLine--  , withSuccessColor-  , withPendingColor-  , withFailColor-  )---silent :: Formatter-silent = Formatter {-  formatterName       = "silent"-, exampleGroupStarted = \_ _ -> return ()-, exampleSucceeded    = \_ _ -> return ()-, exampleFailed       = \_ _ _ -> return ()-, examplePending      = \_ _ _  -> return ()-, failedFormatter     = return ()-, footerFormatter     = return ()-}---specdoc :: Formatter-specdoc = silent {-  formatterName = "specdoc"--, exampleGroupStarted = \nesting name -> do-    writeLine ("\n" ++ indentationForGroup nesting ++ name)--, exampleSucceeded = \nesting requirement -> withSuccessColor $ do-    writeLine $ indentationForExample nesting ++ " - " ++ requirement--, exampleFailed = \nesting requirement _ -> withFailColor $ do-    n <- getFailCount-    writeLine $ indentationForExample nesting ++ " - " ++ requirement ++ " FAILED [" ++ show n ++ "]"--, examplePending = \nesting requirement reason -> withPendingColor $ do-    writeLine $ indentationForExample nesting ++ " - " ++ requirement ++ "\n     # PENDING: " ++ fromMaybe "No reason given" reason--, failedFormatter = defaultFailedFormatter--, footerFormatter = defaultFooter-} where-    indentationForExample nesting = replicate (pred nesting * 2) ' '-    indentationForGroup nesting = replicate (nesting * 2) ' '---progress :: Formatter-progress = silent {-  formatterName    = "progress"-, exampleSucceeded = \_ _ -> withSuccessColor $ write "."-, exampleFailed    = \_ _ _ -> withFailColor    $ write "F"-, examplePending   = \_ _ _ -> withPendingColor $ write "."-, failedFormatter  = defaultFailedFormatter-, footerFormatter  = defaultFooter-}---failed_examples :: Formatter-failed_examples   = silent {-  formatterName   = "failed_examples"-, failedFormatter = defaultFailedFormatter-, footerFormatter = defaultFooter-}---defaultFailedFormatter :: FormatM ()-defaultFailedFormatter = withFailColor $ do-  failures <- getFailMessages-  mapM_ writeLine ("" : intersperse "" failures)-  unless (null failures) (writeLine "")--defaultFooter :: FormatM ()-defaultFooter = do--  writeLine =<< printf "Finished in %1.4f seconds, used %1.4f seconds of CPU time" <$> getRealTime <*> getCPUTime--  fails <- getFailCount-  total <- getTotalCount-  (if fails == 0 then withSuccessColor else withFailColor) $ do-    writeLine ""-    write $ quantify total "example" ++ ", "-    writeLine $ quantify fails "failure"
− Test/Hspec/Formatters/Internal.hs
@@ -1,193 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-module Test.Hspec.Formatters.Internal (---- * Public API-  Formatter (..)-, FormatM--, getSuccessCount-, getPendingCount-, getFailCount-, getTotalCount-, getFailMessages-, getCPUTime-, getRealTime--, write-, writeLine--, withSuccessColor-, withPendingColor-, withFailColor---- * Functions for internal use-, runFormatM-, liftIO-, increaseSuccessCount-, increasePendingCount-, increaseFailCount-, addFailMessage-) where--import qualified System.IO as IO-import           System.IO (Handle)-import           Control.Monad (when)-import           Control.Applicative-import           Control.Exception (bracket_)-import           System.Console.ANSI-import           Control.Monad.Trans.State hiding (gets, modify)-import qualified Control.Monad.Trans.State as State-import qualified Control.Monad.IO.Class as IOClass-import qualified System.CPUTime as CPUTime-import           Data.Time.Clock.POSIX (POSIXTime, getPOSIXTime)---- | A lifted version of `State.gets`-gets :: (FormatterState -> a) -> FormatM a-gets f = FormatM (State.gets f)---- | A lifted version of `State.modify`-modify :: (FormatterState -> FormatterState) -> FormatM ()-modify f = FormatM (State.modify 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)--data FormatterState = FormatterState {-  stateHandle   :: Handle-, stateUseColor :: Bool-, successCount  :: Int-, pendingCount  :: Int-, failCount     :: Int-, failMessages  :: [String]-, cpuStartTime  :: Integer-, startTime     :: POSIXTime-}---- | The total number of examples encountered so far.-totalCount :: FormatterState -> Int-totalCount s = successCount s + pendingCount s + failCount s--newtype FormatM a = FormatM (StateT FormatterState IO a)-  deriving (Functor, Applicative, Monad)--runFormatM :: Bool -> Handle -> FormatM a -> IO a-runFormatM useColor handle (FormatM action) = do-  time <- getPOSIXTime-  cpuTime <- CPUTime.getCPUTime-  evalStateT action (FormatterState handle useColor 0 0 0 [] cpuTime time)---- | Increase the counter for successful examples-increaseSuccessCount :: FormatM ()-increaseSuccessCount = modify $ \s -> s {successCount = succ $ successCount s}---- | Increase the counter for pending examples-increasePendingCount :: FormatM ()-increasePendingCount = modify $ \s -> s {pendingCount = succ $ pendingCount s}---- | Increase the counter for failed examples-increaseFailCount :: FormatM ()-increaseFailCount = modify $ \s -> s {failCount = succ $ failCount s}---- | Get the number of successful examples encountered so far.-getSuccessCount :: FormatM Int-getSuccessCount = gets successCount---- | Get the number of pending examples encountered so far.-getPendingCount :: FormatM Int-getPendingCount = gets pendingCount---- | Get the number of failed examples encountered so far.-getFailCount :: FormatM Int-getFailCount = gets failCount---- | Get the total number of examples encountered so far.-getTotalCount :: FormatM Int-getTotalCount = gets totalCount---- | Append to the list of accumulated failure messages.-addFailMessage :: String -> FormatM ()-addFailMessage err = modify $ \s -> s {failMessages = err : failMessages s}---- | Get the list of accumulated failure messages.-getFailMessages :: FormatM [String]-getFailMessages = reverse `fmap` gets failMessages--data Formatter = Formatter {-  formatterName       :: String---- | evaluated before each test group-, exampleGroupStarted :: Int -> String -> FormatM ()--- | evaluated after each successful example-, exampleSucceeded    :: Int -> String -> FormatM ()--- | evaluated after each failed example-, exampleFailed       :: Int -> String -> String -> FormatM ()--- | evaluated after each pending example-, examplePending      :: Int -> String -> Maybe String -> FormatM ()--- | evaluated after a test run-, failedFormatter     :: FormatM ()--- | evaluated after `failuresFormatter`-, footerFormatter     :: FormatM ()-}---- | Append some output to the report.-write :: String -> FormatM ()-write s = do-  h <- gets stateHandle-  liftIO $ IO.hPutStr h s---- | The same as `write`, but adds a newline character.-writeLine :: String -> FormatM ()-writeLine s = do-  h <- gets stateHandle-  liftIO $ IO.hPutStrLn h s---- | Set output color to red, run given action, and finally restore the default--- color.-withFailColor :: FormatM a -> FormatM a-withFailColor = withColor (SetColor Foreground Dull Red)---- | Set output to color green, run given action, and finally restore the--- default color.-withSuccessColor :: FormatM a -> FormatM a-withSuccessColor = withColor (SetColor Foreground Dull Green)---- | Set output color to yellow, run given action, and finally restore the--- default color.-withPendingColor :: FormatM a -> FormatM a-withPendingColor = withColor (SetColor Foreground Dull Yellow)---- | Set a color, run an action, and finally reset colors.-withColor :: SGR -> FormatM a -> FormatM a-withColor color (FormatM action) = FormatM . StateT $ \st -> do-  let useColor = stateUseColor st-      h        = stateHandle st--  bracket_--    -- set color-    (when useColor $ hSetSGR h [color])--    -- reset colors-    (when useColor $ hSetSGR h [Reset])--    -- run action-    (runStateT action st)---- | Get the used CPU time since the test run has been started.-getCPUTime :: FormatM Double-getCPUTime = do-  t1 <- liftIO CPUTime.getCPUTime-  t0 <- gets cpuStartTime-  return (fromIntegral (t1 - t0) / (10.0^(12::Integer)))---- | Get the passed real time since the test run has been started.-getRealTime :: FormatM Double-getRealTime = do-  t1 <- liftIO getPOSIXTime-  t0 <- gets startTime-  return (realToFrac $ t1 - t0)
− Test/Hspec/HUnit.hs
@@ -1,45 +0,0 @@-{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}-{-# OPTIONS -fno-warn-orphans #-}---- |--- Importing this module allows you to use an @HUnit@ `HU.Test` as an example--- for a behavior.  You can use an explicit `HU.TestCase` data constructor or--- use an `HU.Assertion`.  For an @Assertion@, any exception means the example--- failed; otherwise, it's successfull.------ NOTE: Any output from the example to @stdout@ is ignored.  If you need to--- write out for debugging, you can write to @stderr@ or a file handle.------ > import Test.Hspec.Monadic--- > import Test.Hspec.HUnit ()--- > import Test.HUnit--- >--- > main :: IO ()--- > main = hspecX $ do--- >   describe "reverse" $ do--- >     it "reverses a list" $ do--- >       reverse [1, 2, 3] @?= [3, 2, 1]--- >--- >     it "gives the original list, if applied twice" $ TestCase $--- >       (reverse . reverse) [1, 2, 3] @?= [1, 2, 3]----module Test.Hspec.HUnit () where--import           System.IO.Silently-import           Test.Hspec.Core-import qualified Test.HUnit as HU-import           Data.List (intersperse)--instance Example HU.Assertion where-  evaluateExample io = evaluateExample (HU.TestCase io)--instance Example HU.Test where-  evaluateExample test = do-    (counts, fails) <- silence $ HU.runTestText HU.putTextToShowS test-    let r = if HU.errors counts + HU.failures counts == 0-             then Success-             else Fail (details $ fails "")-    return r-    where-      details :: String -> String-      details = concat . intersperse "\n" . tail . init . lines
− Test/Hspec/Internal.hs
@@ -1,86 +0,0 @@-module Test.Hspec.Internal (-  Spec (..)-, Specs-, Example (..)-, safeEvaluateExample-, Result (..)--, describe-, it--, quantify-)-where--import           Control.Exception---- | A list of specs.-type Specs = [Spec]---- | The result of running an example.-data Result = Success | Pending (Maybe String) | Fail String-  deriving (Eq, Show)---- | Internal representation of a spec.-data Spec = SpecGroup String [Spec]-          | SpecExample String (IO Result)---- | The @describe@ function combines a list of specs into a larger spec.-describe :: String -> [Spec] -> Spec-describe = SpecGroup--safeEvaluateExample :: IO Result -> IO Result-safeEvaluateExample action = do-  action `catches` [-    -- Re-throw AsyncException, otherwise execution will not terminate on-    -- SIGINT (ctrl-c).  All AsyncExceptions are re-thrown (not just-    -- UserInterrupt) because all of them indicate severe conditions and-    -- should not occur during normal test runs.-    Handler (\e -> throw (e :: AsyncException)),--    Handler (\e -> return $ Fail (show (e :: SomeException)))-    ]----- | Create a set of specifications for a specific type being described.--- Once you know what you want specs for, use this.------ > describe "abs" [--- >   it "returns a positive number given a negative number"--- >     (abs (-1) == 1)--- >   ]----it :: Example a => String -> a -> Spec-it requirement = SpecExample requirement . evaluateExample---- | A type class for examples.------ To use an HUnit `Test.HUnit.Test` or an `Test.HUnit.Assertion` as an example--- you need to import "Test.Hspec.HUnit".------ To use a QuickCheck `Test.QuickCheck.Property` as an example you need to--- import "Test.Hspec.QuickCheck".-class Example a where-  evaluateExample :: a -> IO Result--instance Example Bool where-  evaluateExample b = if b then return Success else return (Fail "")--instance Example Result where-  evaluateExample r = r `seq` return r---- | Create a more readable display of a quantity of something.------ Examples:------ >>> quantify 0 "example"--- "0 examples"------ >>> quantify 1 "example"--- "1 example"------ >>> quantify 2 "example"--- "2 examples"-quantify :: Int -> String -> String-quantify 1 s = "1 " ++ s-quantify n s = show n ++ " " ++ s ++ "s"
− Test/Hspec/Monadic.hs
@@ -1,204 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# OPTIONS_HADDOCK prune #-}-{-# OPTIONS_GHC -fno-warn-deprecations #-}--- |--- Hspec is a Behaviour-Driven Development tool for Haskell programmers. BDD is--- an approach to software development that combines Test-Driven Development,--- Domain Driven Design, and Acceptance Test-Driven Planning. Hspec helps you--- do the TDD part of that equation, focusing on the documentation and design--- aspects of TDD.------ Hspec (and the preceding intro) are based on the Ruby library RSpec. Much of--- what applies to RSpec also applies to Hspec. Hspec ties together--- /descriptions/ of behavior and /examples/ of that behavior. The examples can--- also be run as tests and the output summarises what needs to be implemented.-module Test.Hspec.Monadic (--- * Introduction--- $intro--- * Types-  Spec-, Example-, Pending---- * Defining a spec-, describe-, context-, it-, pending---- * Running a spec-, hspec-, hspecB-, hHspec-, Summary (..)---- * Interface to the non-monadic API-, runSpecM-, fromSpecList---- deprecated stuff-, Specs-, descriptions-, hspecX-) where--import           System.IO-import           Test.Hspec.Core (Example)-import qualified Test.Hspec.Core as Core-import qualified Test.Hspec.Runner as Runner-import           Test.Hspec.Runner (Summary (..))-import           Test.Hspec.Pending (Pending)-import qualified Test.Hspec.Pending as Pending--import           Control.Monad.Trans.Writer (Writer, execWriter, tell)---- $intro------ The three functions you'll use the most are 'hspecX', 'describe', and 'it'.--- Here is an example of functions that format and unformat phone numbers and--- the specs for them.------ > import Test.Hspec.Monadic--- > import Test.Hspec.QuickCheck--- > import Test.Hspec.HUnit ()--- > import Test.QuickCheck--- > import Test.HUnit--- >--- > main = hspecX mySpecs------ Since the specs are often used to tell you what to implement, it's best to--- start with undefined functions. Once we have some specs, then you can--- implement each behavior one at a time, ensuring that each behavior is met--- and there is no undocumented behavior.------ > unformatPhoneNumber :: String -> String--- > unformatPhoneNumber number = undefined--- >--- > formatPhoneNumber :: String -> String--- > formatPhoneNumber number = undefined------ The 'describe' function takes a list of behaviors and examples bound--- together with the 'it' function------ > mySpecs = describe "unformatPhoneNumber" $ do------ A boolean expression can act as a behavior's example.------ >   it "removes dashes, spaces, and parenthesies" $--- >     unformatPhoneNumber "(555) 555-1234" == "5555551234"--------- The 'pending' function marks a behavior as pending an example. The example--- doesn't count as failing.------ >   it "handles non-US phone numbers" $--- >     pending "need to look up how other cultures format phone numbers"--------- An HUnit 'Test.HUnit.Test' can act as a behavior's example. (must import--- "Test.Hspec.HUnit")------ >   it "removes the \"ext\" prefix of the extension" $ TestCase $ do--- >     let expected = "5555551234135"--- >         actual   = unformatPhoneNumber "(555) 555-1234 ext 135"--- >     expected @?= actual--------- An @IO()@ action is treated like an HUnit 'TestCase'. (must import--- "Test.Hspec.HUnit")------ >   it "converts letters to numbers" $ do--- >     let expected = "6862377"--- >         actual   = unformatPhoneNumber "NUMBERS"--- >     actual @?= expected--------- The 'property' function allows a QuickCheck property to act as an example.--- (must import "Test.Hspec.QuickCheck")------ >   it "can add and remove formatting without changing the number" $ property $--- >     forAll phoneNumber $ \n -> unformatPhoneNumber (formatPhoneNumber n) == n--- >--- > phoneNumber :: Gen String--- > phoneNumber = do--- >   n <- elements [7,10,11,12,13,14,15]--- >   vectorOf n (elements "0123456789")-----type Spec = SpecM ()--newtype SpecM a = SpecM (Writer [Core.Spec] a)-  deriving Monad---- | Create a document of the given spec and write it to stdout.------ Exit the program with `exitSuccess` if all examples passed, with--- `exitFailure` otherwise.-hspec :: Spec -> IO ()-hspec = Runner.hspec . runSpecM---- | Create a document of the given spec and write it to stdout.------ Return `True` if all examples passed, `False` otherwise.-hspecB :: Spec -> IO Bool-hspecB = Runner.hspecB . runSpecM---- | Create a document of the given specs and write it to the given handle.------ > writeReport filename specs = withFile filename WriteMode (\h -> hHspec h specs)----hHspec :: Handle -> Spec -> IO Summary-hHspec h = Runner.hHspec h . runSpecM---- | Convert a monadic spec into a non-monadic spec.-runSpecM :: Spec -> [Core.Spec]-runSpecM (SpecM specs) = execWriter specs---- | Convert a non-monadic spec into a monadic spec.-fromSpecList :: [Core.Spec] -> Spec-fromSpecList = SpecM . tell---- | The @describe@ function combines a list of specs into a larger spec.-describe :: String -> Spec -> Spec-describe label action = SpecM . tell $ [Core.describe label (runSpecM action)]---- | An alias for `describe`.-context :: String -> Spec -> Spec-context = describe---- |--- Create a set of specifications for a specific type being described.  Once--- you know what you want specs for, use this.------ > describe "abs" $ do--- >   it "returns a positive number given a negative number" $--- >     abs (-1) == 1-it :: Example v => String -> v -> Spec-it label action = (SpecM . tell) [Core.it label action]---- | A pending example.------ If you want to report on a behavior but don't have an example yet, use this.------ > describe "fancyFormatter" $ do--- >   it "can format text in a way that everyone likes" $--- >     pending------ You can give an optional reason for why it's pending.------ > describe "fancyFormatter" $ do--- >   it "can format text in a way that everyone likes" $--- >     pending "waiting for clarification from the designers"-pending :: String  -> Pending-pending = Pending.pending--{-# DEPRECATED Specs "use Spec instead" #-}-type Specs = SpecM ()--{-# DEPRECATED descriptions "use sequence_ instead" #-}-descriptions :: [Spec] -> Spec-descriptions = sequence_--{-# DEPRECATED hspecX "use hspec instead" #-}-hspecX :: Spec -> IO a-hspecX = Runner.hspecX . runSpecM
− Test/Hspec/Pending.hs
@@ -1,35 +0,0 @@-{-# LANGUAGE FlexibleInstances #-}-module Test.Hspec.Pending where--import qualified Test.Hspec.Internal as Internal-import           Test.Hspec.Internal (Example(..))---- NOTE: This is defined in a separate packages, because it clashes with--- Result.Pending.---- | A pending example.-newtype Pending = Pending (Maybe String)--instance Example Pending where-  evaluateExample (Pending reason) = evaluateExample (Internal.Pending reason)--instance Example (String -> Pending) where-  evaluateExample _ = evaluateExample (Pending Nothing)---- | A pending example.------ If you want to report on a behavior but don't have an example yet, use this.------ > describe "fancyFormatter" [--- >   it "can format text in a way that everyone likes" $--- >     pending--- > ]------ You can give an optional reason for why it's pending.------ > describe "fancyFormatter" [--- >   it "can format text in a way that everyone likes" $--- >     pending "waiting for clarification from the designers"--- > ]-pending :: String -> Pending-pending = Pending . Just
− Test/Hspec/QuickCheck.hs
@@ -1,44 +0,0 @@-{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}--- |--- Importing this module allows you to use a QuickCheck `QC.Property` as an--- example for a behavior.  Use `QC.property` to turn any `QC.Testable` into a--- @Property@.------ NOTE: Any output from the example to @stdout@ is ignored.  If you need to--- write out for debugging, you can write to @stderr@ or a file handle.------ > import Test.Hspec.Monadic--- > import Test.Hspec.QuickCheck--- >--- > main :: IO ()--- > main = hspecX $ do--- >   describe "reverse" $ do--- >     it "gives the original list, if applied twice" $ property $--- >       \xs -> (reverse . reverse) xs == (xs :: [Int])----module Test.Hspec.QuickCheck (-  QC.property-, prop-) where--import           System.IO.Silently-import           Test.Hspec.Core-import qualified Test.QuickCheck as QC---- just for the prop shortcut-import qualified Test.Hspec.Monadic as DSL---- | Monadic DSL shortcut, use this instead of `DSL.it`.-prop :: QC.Testable t => String -> t -> DSL.Spec-prop n p = DSL.it n (QC.property p)--instance Example QC.Property where-  evaluateExample p = do-    r <- silence $ QC.quickCheckResult p-    return $-      case r of-        QC.Success {}               -> Success-        f@(QC.Failure {})           -> Fail (QC.output f)-        QC.GaveUp {QC.numTests = n} -> Fail ("Gave up after " ++ quantify n "test" )-        QC.NoExpectedFailure {}     -> Fail ("No expected failure")
− Test/Hspec/Runner.hs
@@ -1,113 +0,0 @@--- | This module contains the runners that take a set of specs, evaluate their examples, and--- report to a given handle.----module Test.Hspec.Runner (-  Specs-, hspec-, hspecB-, hHspec-, hHspecWithFormat-, toExitCode--, Summary (..)---- * Deprecated functions-, hspecX-) where--import           Control.Monad (unless, (>=>))-import           Control.Applicative-import           Data.Monoid--import           Test.Hspec.Internal-import           Test.Hspec.Formatters-import           Test.Hspec.Formatters.Internal-import           System.IO-import           System.Exit---- | Evaluate and print the result of checking the spec examples.-runFormatter :: Formatter -> Spec -> FormatM ()-runFormatter formatter = go 0 []-  where-    go :: Int -> [String] -> Spec -> FormatM ()-    go nesting groups (SpecGroup group xs) = do-      exampleGroupStarted formatter nesting group-      mapM_ (go (succ nesting) (group : groups)) xs-    go nesting groups (SpecExample requirement e) = do-      result <- liftIO $ safeEvaluateExample e-      case result of-        Success -> do-          increaseSuccessCount-          exampleSucceeded formatter nesting requirement-        Fail err -> do-          increaseFailCount-          exampleFailed  formatter nesting requirement err-          n <- getFailCount-          addFailMessage $ failureDetails groups requirement err n-        Pending reason -> do-          increasePendingCount-          examplePending formatter nesting requirement reason--failureDetails :: [String] -> String -> String -> Int -> String-failureDetails groups requirement err i =-  show i ++ ") " ++ groups_ ++ requirement ++ " FAILED" ++ err_-  where-    err_-      | null err  = ""-      | otherwise = "\n" ++ err-    groups_ = case groups of-      [x] -> x ++ " "-      _   -> concatMap (++ " - ") (reverse groups)----- | Create a document of the given specs and write it to stdout.------ Exit the program with `exitSuccess` if all examples passed, with--- `exitFailure` otherwise.-hspec :: Specs -> IO ()-hspec = hspecB >=> (`unless` exitFailure)--{-# DEPRECATED hspecX "use hspec instead" #-}-hspecX :: Specs -> IO a-hspecX = hspecB >=> exitWith . toExitCode---- | Create a document of the given specs and write it to stdout.------ Return `True` if all examples passed, `False` otherwise.-hspecB :: Specs -> IO Bool-hspecB = fmap success . hHspec stdout-  where-    success :: Summary -> Bool-    success s = summaryFailures s == 0---- | Create a document of the given specs and write it to the given handle.------ > writeReport filename specs = withFile filename WriteMode (\h -> hHspec h specs)----hHspec :: Handle -> Specs -> IO Summary-hHspec h specs = do-  useColor <- hIsTerminalDevice h-  hHspecWithFormat specdoc useColor h specs---- | Create a document of the given specs and write it to the given handle.--- THIS IS LIKELY TO CHANGE-hHspecWithFormat :: Formatter -> Bool -> Handle -> Specs -> IO Summary-hHspecWithFormat formatter useColor h ss = runFormatM useColor h $ do-  mapM_ (runFormatter formatter) ss-  failedFormatter formatter-  footerFormatter formatter-  Summary <$> getTotalCount <*> getFailCount--toExitCode :: Bool -> ExitCode-toExitCode True  = ExitSuccess-toExitCode False = ExitFailure 1---- | Summary of a test run.-data Summary = Summary {-  summaryExamples :: Int-, summaryFailures :: Int-} deriving (Eq, Show)--instance Monoid Summary where-  mempty = Summary 0 0-  (Summary x1 x2) `mappend` (Summary y1 y2) = Summary (x1 + y1) (x2 + y2)
hspec.cabal view
@@ -1,5 +1,5 @@ name:             hspec-version:          1.2.0+version:          1.2.0.1 license:          BSD3 license-file:     LICENSE copyright:        (c) 2011 Trystan Spangler@@ -29,7 +29,8 @@ Library   ghc-options:       -Wall-+  hs-source-dirs:+      src   build-depends:       base >= 4 && <= 5     , silently >= 1.1.1 && < 2@@ -38,7 +39,6 @@     , transformers >= 0.2.0 && < 0.4.0     , HUnit >= 1 && <= 2     , QuickCheck >= 2.4.0.1 && <= 2.5-   exposed-modules:       Test.Hspec     , Test.Hspec.Core@@ -47,7 +47,6 @@     , Test.Hspec.Formatters     , Test.Hspec.HUnit     , Test.Hspec.QuickCheck-   other-modules:       Test.Hspec.Pending       Test.Hspec.Internal@@ -57,7 +56,7 @@   type:       exitcode-stdio-1.0   hs-source-dirs:-      ., test+      src, test   main-is:       Spec.hs   ghc-options:
+ src/Test/Hspec.hs view
@@ -0,0 +1,129 @@+{-# OPTIONS_HADDOCK prune #-}+-- |+-- Hspec is a Behaviour-Driven Development tool for Haskell programmers. BDD is+-- an approach to software development that combines Test-Driven Development,+-- Domain Driven Design, and Acceptance Test-Driven Planning. Hspec helps you+-- do the TDD part of that equation, focusing on the documentation and design+-- aspects of TDD.+--+-- Hspec (and the preceding intro) are based on the Ruby library RSpec. Much of+-- what applies to RSpec also applies to Hspec. Hspec ties together+-- /descriptions/ of behavior and /examples/ of that behavior. The examples can+-- also be run as tests and the output summarises what needs to be implemented.+--+-- NOTE: There is a monadic and a non-monadic API.  This is the documentation+-- for the non-monadic API.  The monadic API is more stable, so you may prefer+-- it over this one.  For documentation on the monadic API look at+-- "Test.Hspec.Monadic".++module Test.Hspec {-# WARNING "This module will re-export Test.Hspec.Monadic in the future.  Either use Test.Hspec.Core as a drop-in replacement, or migrate your code to the monadic API!" #-} (++-- * Introduction+-- $intro++-- * Types+  Spec+, Specs+, Example+, Pending++-- * Defining a spec+, describe+, it+, pending++-- * Running a spec+, hspec+, hspecB+, hHspec+, Summary (..)++++++-- deprecated functions+, descriptions+, hspecX++++) where++import           Test.Hspec.Core+++++-- $intro+--+-- The three functions you'll use the most are 'hspec', 'describe', and 'it'.+-- Here is an example of functions that format and unformat phone numbers and+-- the specs for them.+--+-- > import Test.Hspec+-- > import Test.Hspec.QuickCheck+-- > import Test.Hspec.HUnit ()+-- > import Test.QuickCheck+-- > import Test.HUnit+-- >+-- > main = hspec mySpecs+--+-- Since the specs are often used to tell you what to implement, it's best to+-- start with undefined functions. Once we have some specs, then you can+-- implement each behavior one at a time, ensuring that each behavior is met+-- and there is no undocumented behavior.+--+-- > unformatPhoneNumber :: String -> String+-- > unformatPhoneNumber number = undefined+-- >+-- > formatPhoneNumber :: String -> String+-- > formatPhoneNumber number = undefined+--+-- The 'describe' function takes a list of behaviors and examples bound+-- together with the 'it' function+--+-- > mySpecs = [describe "unformatPhoneNumber" [+--+-- A boolean expression can act as a behavior's example.+--+-- >   it "removes dashes, spaces, and parenthesies" $+-- >     unformatPhoneNumber "(555) 555-1234" == "5555551234"+-- >   ,+--+-- The 'pending' function marks a behavior as pending an example. The example+-- doesn't count as failing.+--+-- >   it "handles non-US phone numbers" $+-- >     pending "need to look up how other cultures format phone numbers"+-- >   ,+--+-- An HUnit 'Test.HUnit.Test' can act as a behavior's example. (must import+-- "Test.Hspec.HUnit")+--+-- >   it "removes the \"ext\" prefix of the extension" $ TestCase $ do+-- >     let expected = "5555551234135"+-- >         actual   = unformatPhoneNumber "(555) 555-1234 ext 135"+-- >     expected @?= actual+-- >   ,+--+-- An @IO()@ action is treated like an HUnit 'TestCase'. (must import+-- "Test.Hspec.HUnit")+--+-- >   it "converts letters to numbers" $ do+-- >     let expected = "6862377"+-- >         actual   = unformatPhoneNumber "NUMBERS"+-- >     actual @?= expected+-- >   ,+--+-- The 'property' function allows a QuickCheck property to act as an example.+-- (must import "Test.Hspec.QuickCheck")+--+-- >   it "can add and remove formatting without changing the number" $ property $+-- >     forAll phoneNumber $ \n -> unformatPhoneNumber (formatPhoneNumber n) == n+-- >   ]]+-- >+-- > phoneNumber :: Gen String+-- > phoneNumber = do+-- >   n <- elements [7,10,11,12,13,14,15]+-- >   vectorOf n (elements "0123456789")
+ src/Test/Hspec/Core.hs view
@@ -0,0 +1,144 @@+{-# OPTIONS_HADDOCK prune #-}+-- |+-- Hspec is a Behaviour-Driven Development tool for Haskell programmers. BDD is+-- an approach to software development that combines Test-Driven Development,+-- Domain Driven Design, and Acceptance Test-Driven Planning. Hspec helps you+-- do the TDD part of that equation, focusing on the documentation and design+-- aspects of TDD.+--+-- Hspec (and the preceding intro) are based on the Ruby library RSpec. Much of+-- what applies to RSpec also applies to Hspec. Hspec ties together+-- /descriptions/ of behavior and /examples/ of that behavior. The examples can+-- also be run as tests and the output summarises what needs to be implemented.+--+-- NOTE: There is a monadic and a non-monadic API.  This is the documentation+-- for the non-monadic API.  The monadic API is more stable, so you may prefer+-- it over this one.  For documentation on the monadic API look at+-- "Test.Hspec.Monadic".++module Test.Hspec.Core (+++-- * Introduction+-- $intro++-- * Types+  Spec+, Specs+, Example (..)+, Pending++-- * Defining a spec+, describe+, it+, pending++-- * Running a spec+, hspec+, hspecB+, hHspec+, Summary (..)++-- * Internals+, quantify+, Result (..)++-- deprecated stuff+, descriptions+, hspecX+, AnyExample+, safeEvaluateExample+, UnevaluatedSpec+) where++import           Test.Hspec.Internal hiding (safeEvaluateExample)+import qualified Test.Hspec.Internal as Internal+import           Test.Hspec.Pending+import           Test.Hspec.Runner++-- $intro+--+-- The three functions you'll use the most are 'hspec', 'describe', and 'it'.+-- Here is an example of functions that format and unformat phone numbers and+-- the specs for them.+--+-- > import Test.Hspec+-- > import Test.Hspec.QuickCheck+-- > import Test.Hspec.HUnit ()+-- > import Test.QuickCheck+-- > import Test.HUnit+-- >+-- > main = hspec mySpecs+--+-- Since the specs are often used to tell you what to implement, it's best to+-- start with undefined functions. Once we have some specs, then you can+-- implement each behavior one at a time, ensuring that each behavior is met+-- and there is no undocumented behavior.+--+-- > unformatPhoneNumber :: String -> String+-- > unformatPhoneNumber number = undefined+-- >+-- > formatPhoneNumber :: String -> String+-- > formatPhoneNumber number = undefined+--+-- The 'describe' function takes a list of behaviors and examples bound+-- together with the 'it' function+--+-- > mySpecs = [describe "unformatPhoneNumber" [+--+-- A boolean expression can act as a behavior's example.+--+-- >   it "removes dashes, spaces, and parenthesies" $+-- >     unformatPhoneNumber "(555) 555-1234" == "5555551234"+-- >   ,+--+-- The 'pending' function marks a behavior as pending an example. The example+-- doesn't count as failing.+--+-- >   it "handles non-US phone numbers" $+-- >     pending "need to look up how other cultures format phone numbers"+-- >   ,+--+-- An HUnit 'Test.HUnit.Test' can act as a behavior's example. (must import+-- "Test.Hspec.HUnit")+--+-- >   it "removes the \"ext\" prefix of the extension" $ TestCase $ do+-- >     let expected = "5555551234135"+-- >         actual   = unformatPhoneNumber "(555) 555-1234 ext 135"+-- >     expected @?= actual+-- >   ,+--+-- An @IO()@ action is treated like an HUnit 'TestCase'. (must import+-- "Test.Hspec.HUnit")+--+-- >   it "converts letters to numbers" $ do+-- >     let expected = "6862377"+-- >         actual   = unformatPhoneNumber "NUMBERS"+-- >     actual @?= expected+-- >   ,+--+-- The 'property' function allows a QuickCheck property to act as an example.+-- (must import "Test.Hspec.QuickCheck")+--+-- >   it "can add and remove formatting without changing the number" $ property $+-- >     forAll phoneNumber $ \n -> unformatPhoneNumber (formatPhoneNumber n) == n+-- >   ]]+-- >+-- > phoneNumber :: Gen String+-- > phoneNumber = do+-- >   n <- elements [7,10,11,12,13,14,15]+-- >   vectorOf n (elements "0123456789")++{-# DEPRECATED UnevaluatedSpec "use Spec instead" #-}+type UnevaluatedSpec = Spec++{-# DEPRECATED descriptions "this is no longer needed, and will be removed in a future release" #-}+descriptions :: Specs -> Specs+descriptions = id++{-# DEPRECATED AnyExample "This will be removed with the next major release.  If you still need this, raise your voice!" #-}+type AnyExample  = IO Result++{-# DEPRECATED safeEvaluateExample "This will be removed with the next major release.  If you still need this, raise your voice!" #-}+safeEvaluateExample :: AnyExample -> IO Result+safeEvaluateExample = Internal.safeEvaluateExample
+ src/Test/Hspec/Formatters.hs view
@@ -0,0 +1,146 @@+-- | This module contains formatters that take a set of specs and write to a given handle.+-- They follow a structure similar to RSpec formatters.+--+module Test.Hspec.Formatters (++-- * Formatters+  silent+, specdoc+, progress+, failed_examples++-- * Implementing a custom Formatter+-- |+-- A formatter is a set of actions.  Each action is evaluated when a certain+-- situation is encountered during a test run.+--+-- Actions live in the `FormatM` monad.  It provides access to the runner state+-- and primitives for appending to the generated report.+, Formatter (..)+, FormatM++-- ** Accessing the runner state+, getSuccessCount+, getPendingCount+, getFailCount+, getTotalCount+, getFailMessages+, getCPUTime+, getRealTime++-- ** Appending to the gerenated report+, write+, writeLine++-- ** Dealing with colors+, withSuccessColor+, withPendingColor+, withFailColor+) where++import           Data.Maybe+import           Test.Hspec.Internal (quantify)+import           Data.List (intersperse)+import           Text.Printf+import           Control.Monad (unless)+import           Control.Applicative++-- We use an explicit import list for "Test.Hspec.Formatters.Internal", to make+-- sure, that we only use the public API to implement formatters.+--+-- Everything imported here has to be re-exported, so that users can implement+-- their own formatters.+import Test.Hspec.Formatters.Internal (+    Formatter (..)+  , FormatM++  , getSuccessCount+  , getPendingCount+  , getFailCount+  , getTotalCount+  , getFailMessages+  , getCPUTime+  , getRealTime++  , write+  , writeLine++  , withSuccessColor+  , withPendingColor+  , withFailColor+  )+++silent :: Formatter+silent = Formatter {+  formatterName       = "silent"+, exampleGroupStarted = \_ _ -> return ()+, exampleSucceeded    = \_ _ -> return ()+, exampleFailed       = \_ _ _ -> return ()+, examplePending      = \_ _ _  -> return ()+, failedFormatter     = return ()+, footerFormatter     = return ()+}+++specdoc :: Formatter+specdoc = silent {+  formatterName = "specdoc"++, exampleGroupStarted = \nesting name -> do+    writeLine ("\n" ++ indentationForGroup nesting ++ name)++, exampleSucceeded = \nesting requirement -> withSuccessColor $ do+    writeLine $ indentationForExample nesting ++ " - " ++ requirement++, exampleFailed = \nesting requirement _ -> withFailColor $ do+    n <- getFailCount+    writeLine $ indentationForExample nesting ++ " - " ++ requirement ++ " FAILED [" ++ show n ++ "]"++, examplePending = \nesting requirement reason -> withPendingColor $ do+    writeLine $ indentationForExample nesting ++ " - " ++ requirement ++ "\n     # PENDING: " ++ fromMaybe "No reason given" reason++, failedFormatter = defaultFailedFormatter++, footerFormatter = defaultFooter+} where+    indentationForExample nesting = replicate (pred nesting * 2) ' '+    indentationForGroup nesting = replicate (nesting * 2) ' '+++progress :: Formatter+progress = silent {+  formatterName    = "progress"+, exampleSucceeded = \_ _ -> withSuccessColor $ write "."+, exampleFailed    = \_ _ _ -> withFailColor    $ write "F"+, examplePending   = \_ _ _ -> withPendingColor $ write "."+, failedFormatter  = defaultFailedFormatter+, footerFormatter  = defaultFooter+}+++failed_examples :: Formatter+failed_examples   = silent {+  formatterName   = "failed_examples"+, failedFormatter = defaultFailedFormatter+, footerFormatter = defaultFooter+}+++defaultFailedFormatter :: FormatM ()+defaultFailedFormatter = withFailColor $ do+  failures <- getFailMessages+  mapM_ writeLine ("" : intersperse "" failures)+  unless (null failures) (writeLine "")++defaultFooter :: FormatM ()+defaultFooter = do++  writeLine =<< printf "Finished in %1.4f seconds, used %1.4f seconds of CPU time" <$> getRealTime <*> getCPUTime++  fails <- getFailCount+  total <- getTotalCount+  (if fails == 0 then withSuccessColor else withFailColor) $ do+    writeLine ""+    write $ quantify total "example" ++ ", "+    writeLine $ quantify fails "failure"
+ src/Test/Hspec/Formatters/Internal.hs view
@@ -0,0 +1,193 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+module Test.Hspec.Formatters.Internal (++-- * Public API+  Formatter (..)+, FormatM++, getSuccessCount+, getPendingCount+, getFailCount+, getTotalCount+, getFailMessages+, getCPUTime+, getRealTime++, write+, writeLine++, withSuccessColor+, withPendingColor+, withFailColor++-- * Functions for internal use+, runFormatM+, liftIO+, increaseSuccessCount+, increasePendingCount+, increaseFailCount+, addFailMessage+) where++import qualified System.IO as IO+import           System.IO (Handle)+import           Control.Monad (when)+import           Control.Applicative+import           Control.Exception (bracket_)+import           System.Console.ANSI+import           Control.Monad.Trans.State hiding (gets, modify)+import qualified Control.Monad.Trans.State as State+import qualified Control.Monad.IO.Class as IOClass+import qualified System.CPUTime as CPUTime+import           Data.Time.Clock.POSIX (POSIXTime, getPOSIXTime)++-- | A lifted version of `State.gets`+gets :: (FormatterState -> a) -> FormatM a+gets f = FormatM (State.gets f)++-- | A lifted version of `State.modify`+modify :: (FormatterState -> FormatterState) -> FormatM ()+modify f = FormatM (State.modify 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)++data FormatterState = FormatterState {+  stateHandle   :: Handle+, stateUseColor :: Bool+, successCount  :: Int+, pendingCount  :: Int+, failCount     :: Int+, failMessages  :: [String]+, cpuStartTime  :: Integer+, startTime     :: POSIXTime+}++-- | The total number of examples encountered so far.+totalCount :: FormatterState -> Int+totalCount s = successCount s + pendingCount s + failCount s++newtype FormatM a = FormatM (StateT FormatterState IO a)+  deriving (Functor, Applicative, Monad)++runFormatM :: Bool -> Handle -> FormatM a -> IO a+runFormatM useColor handle (FormatM action) = do+  time <- getPOSIXTime+  cpuTime <- CPUTime.getCPUTime+  evalStateT action (FormatterState handle useColor 0 0 0 [] cpuTime time)++-- | Increase the counter for successful examples+increaseSuccessCount :: FormatM ()+increaseSuccessCount = modify $ \s -> s {successCount = succ $ successCount s}++-- | Increase the counter for pending examples+increasePendingCount :: FormatM ()+increasePendingCount = modify $ \s -> s {pendingCount = succ $ pendingCount s}++-- | Increase the counter for failed examples+increaseFailCount :: FormatM ()+increaseFailCount = modify $ \s -> s {failCount = succ $ failCount s}++-- | Get the number of successful examples encountered so far.+getSuccessCount :: FormatM Int+getSuccessCount = gets successCount++-- | Get the number of pending examples encountered so far.+getPendingCount :: FormatM Int+getPendingCount = gets pendingCount++-- | Get the number of failed examples encountered so far.+getFailCount :: FormatM Int+getFailCount = gets failCount++-- | Get the total number of examples encountered so far.+getTotalCount :: FormatM Int+getTotalCount = gets totalCount++-- | Append to the list of accumulated failure messages.+addFailMessage :: String -> FormatM ()+addFailMessage err = modify $ \s -> s {failMessages = err : failMessages s}++-- | Get the list of accumulated failure messages.+getFailMessages :: FormatM [String]+getFailMessages = reverse `fmap` gets failMessages++data Formatter = Formatter {+  formatterName       :: String++-- | evaluated before each test group+, exampleGroupStarted :: Int -> String -> FormatM ()+-- | evaluated after each successful example+, exampleSucceeded    :: Int -> String -> FormatM ()+-- | evaluated after each failed example+, exampleFailed       :: Int -> String -> String -> FormatM ()+-- | evaluated after each pending example+, examplePending      :: Int -> String -> Maybe String -> FormatM ()+-- | evaluated after a test run+, failedFormatter     :: FormatM ()+-- | evaluated after `failuresFormatter`+, footerFormatter     :: FormatM ()+}++-- | Append some output to the report.+write :: String -> FormatM ()+write s = do+  h <- gets stateHandle+  liftIO $ IO.hPutStr h s++-- | The same as `write`, but adds a newline character.+writeLine :: String -> FormatM ()+writeLine s = do+  h <- gets stateHandle+  liftIO $ IO.hPutStrLn h s++-- | Set output color to red, run given action, and finally restore the default+-- color.+withFailColor :: FormatM a -> FormatM a+withFailColor = withColor (SetColor Foreground Dull Red)++-- | Set output to color green, run given action, and finally restore the+-- default color.+withSuccessColor :: FormatM a -> FormatM a+withSuccessColor = withColor (SetColor Foreground Dull Green)++-- | Set output color to yellow, run given action, and finally restore the+-- default color.+withPendingColor :: FormatM a -> FormatM a+withPendingColor = withColor (SetColor Foreground Dull Yellow)++-- | Set a color, run an action, and finally reset colors.+withColor :: SGR -> FormatM a -> FormatM a+withColor color (FormatM action) = FormatM . StateT $ \st -> do+  let useColor = stateUseColor st+      h        = stateHandle st++  bracket_++    -- set color+    (when useColor $ hSetSGR h [color])++    -- reset colors+    (when useColor $ hSetSGR h [Reset])++    -- run action+    (runStateT action st)++-- | Get the used CPU time since the test run has been started.+getCPUTime :: FormatM Double+getCPUTime = do+  t1 <- liftIO CPUTime.getCPUTime+  t0 <- gets cpuStartTime+  return (fromIntegral (t1 - t0) / (10.0^(12::Integer)))++-- | Get the passed real time since the test run has been started.+getRealTime :: FormatM Double+getRealTime = do+  t1 <- liftIO getPOSIXTime+  t0 <- gets startTime+  return (realToFrac $ t1 - t0)
+ src/Test/Hspec/HUnit.hs view
@@ -0,0 +1,45 @@+{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}+{-# OPTIONS -fno-warn-orphans #-}++-- |+-- Importing this module allows you to use an @HUnit@ `HU.Test` as an example+-- for a behavior.  You can use an explicit `HU.TestCase` data constructor or+-- use an `HU.Assertion`.  For an @Assertion@, any exception means the example+-- failed; otherwise, it's successfull.+--+-- NOTE: Any output from the example to @stdout@ is ignored.  If you need to+-- write out for debugging, you can write to @stderr@ or a file handle.+--+-- > import Test.Hspec.Monadic+-- > import Test.Hspec.HUnit ()+-- > import Test.HUnit+-- >+-- > main :: IO ()+-- > main = hspec $ do+-- >   describe "reverse" $ do+-- >     it "reverses a list" $ do+-- >       reverse [1, 2, 3] @?= [3, 2, 1]+-- >+-- >     it "gives the original list, if applied twice" $ TestCase $+-- >       (reverse . reverse) [1, 2, 3] @?= [1, 2, 3]+--+module Test.Hspec.HUnit () where++import           System.IO.Silently+import           Test.Hspec.Core+import qualified Test.HUnit as HU+import           Data.List (intersperse)++instance Example HU.Assertion where+  evaluateExample io = evaluateExample (HU.TestCase io)++instance Example HU.Test where+  evaluateExample test = do+    (counts, fails) <- silence $ HU.runTestText HU.putTextToShowS test+    let r = if HU.errors counts + HU.failures counts == 0+             then Success+             else Fail (details $ fails "")+    return r+    where+      details :: String -> String+      details = concat . intersperse "\n" . tail . init . lines
+ src/Test/Hspec/Internal.hs view
@@ -0,0 +1,86 @@+module Test.Hspec.Internal (+  Spec (..)+, Specs+, Example (..)+, safeEvaluateExample+, Result (..)++, describe+, it++, quantify+)+where++import           Control.Exception++-- | A list of specs.+type Specs = [Spec]++-- | The result of running an example.+data Result = Success | Pending (Maybe String) | Fail String+  deriving (Eq, Show)++-- | Internal representation of a spec.+data Spec = SpecGroup String [Spec]+          | SpecExample String (IO Result)++-- | The @describe@ function combines a list of specs into a larger spec.+describe :: String -> [Spec] -> Spec+describe = SpecGroup++safeEvaluateExample :: IO Result -> IO Result+safeEvaluateExample action = do+  action `catches` [+    -- Re-throw AsyncException, otherwise execution will not terminate on+    -- SIGINT (ctrl-c).  All AsyncExceptions are re-thrown (not just+    -- UserInterrupt) because all of them indicate severe conditions and+    -- should not occur during normal test runs.+    Handler (\e -> throw (e :: AsyncException)),++    Handler (\e -> return $ Fail (show (e :: SomeException)))+    ]+++-- | Create a set of specifications for a specific type being described.+-- Once you know what you want specs for, use this.+--+-- > describe "abs" [+-- >   it "returns a positive number given a negative number"+-- >     (abs (-1) == 1)+-- >   ]+--+it :: Example a => String -> a -> Spec+it requirement = SpecExample requirement . evaluateExample++-- | A type class for examples.+--+-- To use an HUnit `Test.HUnit.Test` or an `Test.HUnit.Assertion` as an example+-- you need to import "Test.Hspec.HUnit".+--+-- To use a QuickCheck `Test.QuickCheck.Property` as an example you need to+-- import "Test.Hspec.QuickCheck".+class Example a where+  evaluateExample :: a -> IO Result++instance Example Bool where+  evaluateExample b = if b then return Success else return (Fail "")++instance Example Result where+  evaluateExample r = r `seq` return r++-- | Create a more readable display of a quantity of something.+--+-- Examples:+--+-- >>> quantify 0 "example"+-- "0 examples"+--+-- >>> quantify 1 "example"+-- "1 example"+--+-- >>> quantify 2 "example"+-- "2 examples"+quantify :: Int -> String -> String+quantify 1 s = "1 " ++ s+quantify n s = show n ++ " " ++ s ++ "s"
+ src/Test/Hspec/Monadic.hs view
@@ -0,0 +1,204 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# OPTIONS_HADDOCK prune #-}+{-# OPTIONS_GHC -fno-warn-deprecations #-}+-- |+-- Hspec is a Behaviour-Driven Development tool for Haskell programmers. BDD is+-- an approach to software development that combines Test-Driven Development,+-- Domain Driven Design, and Acceptance Test-Driven Planning. Hspec helps you+-- do the TDD part of that equation, focusing on the documentation and design+-- aspects of TDD.+--+-- Hspec (and the preceding intro) are based on the Ruby library RSpec. Much of+-- what applies to RSpec also applies to Hspec. Hspec ties together+-- /descriptions/ of behavior and /examples/ of that behavior. The examples can+-- also be run as tests and the output summarises what needs to be implemented.+module Test.Hspec.Monadic (+-- * Introduction+-- $intro+-- * Types+  Spec+, Example+, Pending++-- * Defining a spec+, describe+, context+, it+, pending++-- * Running a spec+, hspec+, hspecB+, hHspec+, Summary (..)++-- * Interface to the non-monadic API+, runSpecM+, fromSpecList++-- deprecated stuff+, Specs+, descriptions+, hspecX+) where++import           System.IO+import           Test.Hspec.Core (Example)+import qualified Test.Hspec.Core as Core+import qualified Test.Hspec.Runner as Runner+import           Test.Hspec.Runner (Summary (..))+import           Test.Hspec.Pending (Pending)+import qualified Test.Hspec.Pending as Pending++import           Control.Monad.Trans.Writer (Writer, execWriter, tell)++-- $intro+--+-- The three functions you'll use the most are 'hspec', 'describe', and 'it'.+-- Here is an example of functions that format and unformat phone numbers and+-- the specs for them.+--+-- > import Test.Hspec.Monadic+-- > import Test.Hspec.QuickCheck+-- > import Test.Hspec.HUnit ()+-- > import Test.QuickCheck+-- > import Test.HUnit+-- >+-- > main = hspec mySpecs+--+-- Since the specs are often used to tell you what to implement, it's best to+-- start with undefined functions. Once we have some specs, then you can+-- implement each behavior one at a time, ensuring that each behavior is met+-- and there is no undocumented behavior.+--+-- > unformatPhoneNumber :: String -> String+-- > unformatPhoneNumber number = undefined+-- >+-- > formatPhoneNumber :: String -> String+-- > formatPhoneNumber number = undefined+--+-- The 'describe' function takes a list of behaviors and examples bound+-- together with the 'it' function+--+-- > mySpecs = describe "unformatPhoneNumber" $ do+--+-- A boolean expression can act as a behavior's example.+--+-- >   it "removes dashes, spaces, and parenthesies" $+-- >     unformatPhoneNumber "(555) 555-1234" == "5555551234"+--+--+-- The 'pending' function marks a behavior as pending an example. The example+-- doesn't count as failing.+--+-- >   it "handles non-US phone numbers" $+-- >     pending "need to look up how other cultures format phone numbers"+--+--+-- An HUnit 'Test.HUnit.Test' can act as a behavior's example. (must import+-- "Test.Hspec.HUnit")+--+-- >   it "removes the \"ext\" prefix of the extension" $ TestCase $ do+-- >     let expected = "5555551234135"+-- >         actual   = unformatPhoneNumber "(555) 555-1234 ext 135"+-- >     expected @?= actual+--+--+-- An @IO()@ action is treated like an HUnit 'TestCase'. (must import+-- "Test.Hspec.HUnit")+--+-- >   it "converts letters to numbers" $ do+-- >     let expected = "6862377"+-- >         actual   = unformatPhoneNumber "NUMBERS"+-- >     actual @?= expected+--+--+-- The 'property' function allows a QuickCheck property to act as an example.+-- (must import "Test.Hspec.QuickCheck")+--+-- >   it "can add and remove formatting without changing the number" $ property $+-- >     forAll phoneNumber $ \n -> unformatPhoneNumber (formatPhoneNumber n) == n+-- >+-- > phoneNumber :: Gen String+-- > phoneNumber = do+-- >   n <- elements [7,10,11,12,13,14,15]+-- >   vectorOf n (elements "0123456789")+--++type Spec = SpecM ()++newtype SpecM a = SpecM (Writer [Core.Spec] a)+  deriving Monad++-- | Create a document of the given spec and write it to stdout.+--+-- Exit the program with `exitSuccess` if all examples passed, with+-- `exitFailure` otherwise.+hspec :: Spec -> IO ()+hspec = Runner.hspec . runSpecM++-- | Create a document of the given spec and write it to stdout.+--+-- Return `True` if all examples passed, `False` otherwise.+hspecB :: Spec -> IO Bool+hspecB = Runner.hspecB . runSpecM++-- | Create a document of the given specs and write it to the given handle.+--+-- > writeReport filename specs = withFile filename WriteMode (\h -> hHspec h specs)+--+hHspec :: Handle -> Spec -> IO Summary+hHspec h = Runner.hHspec h . runSpecM++-- | Convert a monadic spec into a non-monadic spec.+runSpecM :: Spec -> [Core.Spec]+runSpecM (SpecM specs) = execWriter specs++-- | Convert a non-monadic spec into a monadic spec.+fromSpecList :: [Core.Spec] -> Spec+fromSpecList = SpecM . tell++-- | The @describe@ function combines a list of specs into a larger spec.+describe :: String -> Spec -> Spec+describe label action = SpecM . tell $ [Core.describe label (runSpecM action)]++-- | An alias for `describe`.+context :: String -> Spec -> Spec+context = describe++-- |+-- Create a set of specifications for a specific type being described.  Once+-- you know what you want specs for, use this.+--+-- > describe "abs" $ do+-- >   it "returns a positive number given a negative number" $+-- >     abs (-1) == 1+it :: Example v => String -> v -> Spec+it label action = (SpecM . tell) [Core.it label action]++-- | A pending example.+--+-- If you want to report on a behavior but don't have an example yet, use this.+--+-- > describe "fancyFormatter" $ do+-- >   it "can format text in a way that everyone likes" $+-- >     pending+--+-- You can give an optional reason for why it's pending.+--+-- > describe "fancyFormatter" $ do+-- >   it "can format text in a way that everyone likes" $+-- >     pending "waiting for clarification from the designers"+pending :: String  -> Pending+pending = Pending.pending++{-# DEPRECATED Specs "use Spec instead" #-}+type Specs = SpecM ()++{-# DEPRECATED descriptions "use sequence_ instead" #-}+descriptions :: [Spec] -> Spec+descriptions = sequence_++{-# DEPRECATED hspecX "use hspec instead" #-}+hspecX :: Spec -> IO a+hspecX = Runner.hspecX . runSpecM
+ src/Test/Hspec/Pending.hs view
@@ -0,0 +1,35 @@+{-# LANGUAGE FlexibleInstances #-}+module Test.Hspec.Pending where++import qualified Test.Hspec.Internal as Internal+import           Test.Hspec.Internal (Example(..))++-- NOTE: This is defined in a separate packages, because it clashes with+-- Result.Pending.++-- | A pending example.+newtype Pending = Pending (Maybe String)++instance Example Pending where+  evaluateExample (Pending reason) = evaluateExample (Internal.Pending reason)++instance Example (String -> Pending) where+  evaluateExample _ = evaluateExample (Pending Nothing)++-- | A pending example.+--+-- If you want to report on a behavior but don't have an example yet, use this.+--+-- > describe "fancyFormatter" [+-- >   it "can format text in a way that everyone likes" $+-- >     pending+-- > ]+--+-- You can give an optional reason for why it's pending.+--+-- > describe "fancyFormatter" [+-- >   it "can format text in a way that everyone likes" $+-- >     pending "waiting for clarification from the designers"+-- > ]+pending :: String -> Pending+pending = Pending . Just
+ src/Test/Hspec/QuickCheck.hs view
@@ -0,0 +1,44 @@+{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+-- |+-- Importing this module allows you to use a QuickCheck `QC.Property` as an+-- example for a behavior.  Use `QC.property` to turn any `QC.Testable` into a+-- @Property@.+--+-- NOTE: Any output from the example to @stdout@ is ignored.  If you need to+-- write out for debugging, you can write to @stderr@ or a file handle.+--+-- > import Test.Hspec.Monadic+-- > import Test.Hspec.QuickCheck+-- >+-- > main :: IO ()+-- > main = hspec $ do+-- >   describe "reverse" $ do+-- >     it "gives the original list, if applied twice" $ property $+-- >       \xs -> (reverse . reverse) xs == (xs :: [Int])+--+module Test.Hspec.QuickCheck (+  QC.property+, prop+) where++import           System.IO.Silently+import           Test.Hspec.Core+import qualified Test.QuickCheck as QC++-- just for the prop shortcut+import qualified Test.Hspec.Monadic as DSL++-- | Monadic DSL shortcut, use this instead of `DSL.it`.+prop :: QC.Testable t => String -> t -> DSL.Spec+prop n p = DSL.it n (QC.property p)++instance Example QC.Property where+  evaluateExample p = do+    r <- silence $ QC.quickCheckResult p+    return $+      case r of+        QC.Success {}               -> Success+        f@(QC.Failure {})           -> Fail (QC.output f)+        QC.GaveUp {QC.numTests = n} -> Fail ("Gave up after " ++ quantify n "test" )+        QC.NoExpectedFailure {}     -> Fail ("No expected failure")
+ src/Test/Hspec/Runner.hs view
@@ -0,0 +1,113 @@+-- | This module contains the runners that take a set of specs, evaluate their examples, and+-- report to a given handle.+--+module Test.Hspec.Runner (+  Specs+, hspec+, hspecB+, hHspec+, hHspecWithFormat+, toExitCode++, Summary (..)++-- * Deprecated functions+, hspecX+) where++import           Control.Monad (unless, (>=>))+import           Control.Applicative+import           Data.Monoid++import           Test.Hspec.Internal+import           Test.Hspec.Formatters+import           Test.Hspec.Formatters.Internal+import           System.IO+import           System.Exit++-- | Evaluate and print the result of checking the spec examples.+runFormatter :: Formatter -> Spec -> FormatM ()+runFormatter formatter = go 0 []+  where+    go :: Int -> [String] -> Spec -> FormatM ()+    go nesting groups (SpecGroup group xs) = do+      exampleGroupStarted formatter nesting group+      mapM_ (go (succ nesting) (group : groups)) xs+    go nesting groups (SpecExample requirement e) = do+      result <- liftIO $ safeEvaluateExample e+      case result of+        Success -> do+          increaseSuccessCount+          exampleSucceeded formatter nesting requirement+        Fail err -> do+          increaseFailCount+          exampleFailed  formatter nesting requirement err+          n <- getFailCount+          addFailMessage $ failureDetails groups requirement err n+        Pending reason -> do+          increasePendingCount+          examplePending formatter nesting requirement reason++failureDetails :: [String] -> String -> String -> Int -> String+failureDetails groups requirement err i =+  show i ++ ") " ++ groups_ ++ requirement ++ " FAILED" ++ err_+  where+    err_+      | null err  = ""+      | otherwise = "\n" ++ err+    groups_ = case groups of+      [x] -> x ++ " "+      _   -> concatMap (++ " - ") (reverse groups)+++-- | Create a document of the given specs and write it to stdout.+--+-- Exit the program with `exitSuccess` if all examples passed, with+-- `exitFailure` otherwise.+hspec :: Specs -> IO ()+hspec = hspecB >=> (`unless` exitFailure)++{-# DEPRECATED hspecX "use hspec instead" #-}+hspecX :: Specs -> IO a+hspecX = hspecB >=> exitWith . toExitCode++-- | Create a document of the given specs and write it to stdout.+--+-- Return `True` if all examples passed, `False` otherwise.+hspecB :: Specs -> IO Bool+hspecB = fmap success . hHspec stdout+  where+    success :: Summary -> Bool+    success s = summaryFailures s == 0++-- | Create a document of the given specs and write it to the given handle.+--+-- > writeReport filename specs = withFile filename WriteMode (\h -> hHspec h specs)+--+hHspec :: Handle -> Specs -> IO Summary+hHspec h specs = do+  useColor <- hIsTerminalDevice h+  hHspecWithFormat specdoc useColor h specs++-- | Create a document of the given specs and write it to the given handle.+-- THIS IS LIKELY TO CHANGE+hHspecWithFormat :: Formatter -> Bool -> Handle -> Specs -> IO Summary+hHspecWithFormat formatter useColor h ss = runFormatM useColor h $ do+  mapM_ (runFormatter formatter) ss+  failedFormatter formatter+  footerFormatter formatter+  Summary <$> getTotalCount <*> getFailCount++toExitCode :: Bool -> ExitCode+toExitCode True  = ExitSuccess+toExitCode False = ExitFailure 1++-- | Summary of a test run.+data Summary = Summary {+  summaryExamples :: Int+, summaryFailures :: Int+} deriving (Eq, Show)++instance Monoid Summary where+  mempty = Summary 0 0+  (Summary x1 x2) `mappend` (Summary y1 y2) = Summary (x1 + y1) (x2 + y2)
test/doctests.hs view
@@ -3,4 +3,4 @@ import           Test.DocTest  main :: IO ()-main = doctest ["Test/Hspec/Internal.hs"]+main = doctest ["--optghc=-isrc", "src/Test/Hspec/Internal.hs"]